import { permissionRequestSelectSql } from "../internal/sql.js";
import { isRecord, mapPermissionRequest } from "../internal/rowMappers.js";
import { clampLimit, decodePermissionRequestCursor, encodePermissionRequestCursor, } from "../internal/cursors.js";
export class PermissionRequestRepository {
    #db;
    #activity;
    constructor(db, activity) {
        this.#db = db;
        this.#activity = activity;
    }
    upsertPermissionRequest(input) {
        const providerIds = { runtimeSessionId: input.runtimeSessionId };
        if (input.providerRequestId)
            providerIds.providerRequestId = input.providerRequestId;
        if (input.providerPermissionId)
            providerIds.providerPermissionId = input.providerPermissionId;
        const existing = this.findPermissionRequestByProviderIds(providerIds);
        if (existing)
            return existing;
        const now = new Date().toISOString();
        this.#db
            .prepare(`INSERT INTO permission_requests
           (id, run_id, workspace_id, runtime_session_id, provider, provider_request_id, provider_permission_id, source_runtime_event_id, request_json, evaluation_json, status, created_at, updated_at)
         VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'pending', ?, ?)`)
            .run(input.id, input.runId ?? null, input.workspaceId, input.runtimeSessionId, input.provider, input.providerRequestId ?? null, input.providerPermissionId ?? null, input.sourceRuntimeEventId ?? null, JSON.stringify(input.request), JSON.stringify(input.evaluation), now, now);
        const request = this.getPermissionRequest(input.id);
        this.#activity.appendPermissionRequestActivityEvent(request, {
            kind: "request_created",
            activityAt: now,
            status: "pending",
        });
        return request;
    }
    findPermissionRequest(id) {
        const request = this.#db
            .prepare(permissionRequestSelectSql("WHERE id = ?"))
            .get(id);
        return isRecord(request) ? mapPermissionRequest(request) : undefined;
    }
    listPermissionRequests(options = {}) {
        const limit = clampLimit(options.limit ?? 50);
        const cursor = decodePermissionRequestCursor(options.cursor);
        const clauses = [];
        const params = [];
        if (options.status) {
            clauses.push("status = ?");
            params.push(options.status);
        }
        if (options.runId) {
            clauses.push("run_id = ?");
            params.push(options.runId);
        }
        if (options.workspaceId) {
            clauses.push("workspace_id = ?");
            params.push(options.workspaceId);
        }
        if (cursor) {
            clauses.push("(created_at > ? OR (created_at = ? AND id > ?))");
            params.push(cursor.createdAt, cursor.createdAt, cursor.id);
        }
        const where = clauses.length > 0 ? `WHERE ${clauses.join(" AND ")}` : "";
        const rows = this.#db
            .prepare(`${permissionRequestSelectSql(where)} ORDER BY created_at ASC, id ASC LIMIT ?`)
            .all(...params, limit + 1);
        const mapped = rows.map(mapPermissionRequest);
        const requests = mapped.slice(0, limit);
        const last = requests.at(-1);
        return {
            requests,
            ...(mapped.length > limit && last
                ? { nextCursor: encodePermissionRequestCursor(last) }
                : {}),
        };
    }
    claimPermissionRequestForReply(id) {
        const now = new Date().toISOString();
        const result = this.#db
            .prepare("UPDATE permission_requests SET status = 'replying', last_error = NULL, updated_at = ? WHERE id = ? AND status IN ('pending', 'reply_failed')")
            .run(now, id);
        if ((result.changes ?? 0) < 1)
            return undefined;
        return this.getPermissionRequest(id);
    }
    markPermissionRequestAnswered(input) {
        const now = new Date().toISOString();
        const result = this.#db
            .prepare(`UPDATE permission_requests
         SET status = 'answered', selected_option = ?, opencode_reply = ?, decision_audit_id = ?, last_error = NULL, answered_at = ?, updated_at = ?
         WHERE id = ? AND status = 'replying'`)
            .run(input.selectedOption, input.opencodeReply, input.decisionAuditId ?? null, now, now, input.id);
        const request = this.getPermissionRequest(input.id);
        if ((result.changes ?? 0) > 0) {
            this.#activity.appendPermissionRequestActivityEvent(request, {
                kind: "request_answered",
                activityAt: now,
                status: "answered",
                selectedOption: request.selectedOption,
                opencodeReply: request.opencodeReply,
            });
        }
        return request;
    }
    markPermissionRequestReplyFailed(id, lastError, selectedOption, opencodeReply) {
        const now = new Date().toISOString();
        const result = this.#db
            .prepare(`UPDATE permission_requests
         SET status = 'reply_failed', selected_option = ?, opencode_reply = ?, last_error = ?, updated_at = ?
         WHERE id = ? AND status = 'replying'`)
            .run(selectedOption, opencodeReply, lastError.slice(0, 2_000), now, id);
        const request = this.getPermissionRequest(id);
        if ((result.changes ?? 0) > 0) {
            this.#activity.appendPermissionRequestActivityEvent(request, {
                kind: "request_reply_failed",
                activityAt: now,
                status: "reply_failed",
                selectedOption: request.selectedOption,
                opencodeReply: request.opencodeReply,
                error: request.lastError,
            });
        }
        return request;
    }
    getPermissionRequest(id) {
        const request = this.#db
            .prepare(permissionRequestSelectSql("WHERE id = ?"))
            .get(id);
        return mapPermissionRequest(request);
    }
    findPermissionRequestByProviderIds(input) {
        if (input.providerPermissionId) {
            const request = this.#db
                .prepare(permissionRequestSelectSql("WHERE runtime_session_id = ? AND provider_permission_id = ?"))
                .get(input.runtimeSessionId, input.providerPermissionId);
            if (isRecord(request))
                return mapPermissionRequest(request);
        }
        if (input.providerRequestId) {
            const request = this.#db
                .prepare(permissionRequestSelectSql("WHERE runtime_session_id = ? AND provider_request_id = ?"))
                .get(input.runtimeSessionId, input.providerRequestId);
            if (isRecord(request))
                return mapPermissionRequest(request);
        }
        return undefined;
    }
}
