import { permissionActivityEventSelectSql, permissionDecisionSelectSql, permissionRequestSelectSql, } from "../internal/sql.js";
import { createPermissionActivityEventId, isPermissionSessionDecisionActivity, mapPermissionActivityEvent, mapPermissionDecision, mapPermissionRequest, normalizeAffectedPathsJson, readPermissionActivityToolCall, readPermissionDenyOnlyDiagnostics, readPermissionExternalMutation, } from "../internal/rowMappers.js";
import { clampLimit, decodePermissionActivityCursor, encodePermissionActivityCursor, } from "../internal/cursors.js";
export class PermissionActivityRepository {
    #db;
    constructor(db) {
        this.#db = db;
    }
    listPermissionActivity(options) {
        const limit = clampLimit(options.limit ?? 50);
        const cursor = decodePermissionActivityCursor(options.cursor);
        const clauses = ["workspace_id = ?"];
        const params = [options.workspaceId];
        if (options.runId) {
            clauses.push("run_id = ?");
            params.push(options.runId);
        }
        if (cursor) {
            clauses.push("(activity_at > ? OR (activity_at = ? AND id > ?))");
            params.push(cursor.at, cursor.at, cursor.id);
        }
        const rows = this.#db
            .prepare(`${permissionActivityEventSelectSql(`WHERE ${clauses.join(" AND ")}`)} ORDER BY activity_at ASC, id ASC LIMIT ?`)
            .all(...params, limit + 1);
        const mapped = rows.map(mapPermissionActivityEvent);
        const events = mapped.slice(0, limit);
        const last = events.at(-1);
        return {
            events,
            ...(mapped.length > limit && last
                ? { nextCursor: encodePermissionActivityCursor(last) }
                : {}),
        };
    }
    appendPermissionRequestActivityEvent(request, input) {
        const toolCall = readPermissionActivityToolCall(request.requestJson);
        const denyOnlyDiagnostics = readPermissionDenyOnlyDiagnostics(request.requestJson);
        this.appendPermissionActivityEvent({
            id: input.eventId ?? createPermissionActivityEventId(),
            kind: input.kind,
            activityAt: input.activityAt,
            workspaceId: request.workspaceId,
            runId: request.runId,
            runtimeSessionId: request.runtimeSessionId,
            requestId: request.id,
            status: input.status,
            selectedOption: input.selectedOption,
            opencodeReply: input.opencodeReply,
            ...toolCall,
            externalServiceMutation: readPermissionExternalMutation(request.requestJson),
            denyOnlyDiagnostics,
            error: input.error,
        });
    }
    appendPermissionDecisionActivityEvent(decision, input) {
        this.appendPermissionActivityEvent({
            id: input.eventId ?? createPermissionActivityEventId(),
            kind: input.kind,
            activityAt: input.activityAt,
            workspaceId: decision.workspaceId,
            runId: decision.runId,
            runtimeSessionId: decision.runtimeSessionId,
            requestId: decision.sourcePermissionRequestId,
            decisionId: decision.id,
            status: input.status,
            selectedOption: decision.option,
            toolName: decision.toolName,
            toolTitle: decision.toolTitle,
            toolRisk: decision.toolRisk,
            inputPreview: decision.inputPreview,
            affectedPathsJson: normalizeAffectedPathsJson(decision.affectedPathsJson),
            revokeReason: input.revokeReason,
            revokeSource: input.revokeSource,
        });
    }
    appendPermissionActivityEvent(input) {
        const now = new Date().toISOString();
        this.#db
            .prepare(`INSERT OR IGNORE INTO permission_activity_events
           (id, kind, activity_at, source, workspace_id, run_id, runtime_session_id, request_id, decision_id, status, selected_option, opencode_reply, tool_name, tool_title, tool_risk, input_preview, affected_paths_json, external_service_mutation, deny_only_diagnostics_json, error, revoke_reason, revoke_source, created_at)
         VALUES (?, ?, ?, 'opencode', ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
            .run(input.id, input.kind, input.activityAt, input.workspaceId, input.runId ?? null, input.runtimeSessionId ?? null, input.requestId ?? null, input.decisionId ?? null, input.status ?? null, input.selectedOption ?? null, input.opencodeReply ?? null, input.toolName.slice(0, 120), input.toolTitle.slice(0, 200), input.toolRisk.slice(0, 50), input.inputPreview.slice(0, 500), normalizeAffectedPathsJson(input.affectedPathsJson), input.externalServiceMutation ? 1 : 0, input.denyOnlyDiagnostics
            ? JSON.stringify(input.denyOnlyDiagnostics)
            : null, input.error ? input.error.slice(0, 2_000) : null, input.revokeReason ? input.revokeReason.slice(0, 500) : null, input.revokeSource ?? null, now);
    }
    backfillPermissionActivityEvents() {
        for (const request of this.#db
            .prepare(permissionRequestSelectSql(""))
            .all()
            .map(mapPermissionRequest)) {
            this.appendPermissionRequestActivityEvent(request, {
                eventId: `pact_backfill_request_created_${request.id}`,
                kind: "request_created",
                activityAt: request.createdAt,
                status: "pending",
            });
            if (request.answeredAt) {
                this.appendPermissionRequestActivityEvent(request, {
                    eventId: `pact_backfill_request_answered_${request.id}`,
                    kind: "request_answered",
                    activityAt: request.answeredAt,
                    status: "answered",
                    selectedOption: request.selectedOption,
                    opencodeReply: request.opencodeReply,
                });
            }
            if (request.status === "reply_failed") {
                this.appendPermissionRequestActivityEvent(request, {
                    eventId: `pact_backfill_request_reply_failed_${request.id}`,
                    kind: "request_reply_failed",
                    activityAt: request.updatedAt,
                    status: "reply_failed",
                    selectedOption: request.selectedOption,
                    opencodeReply: request.opencodeReply,
                    error: request.lastError,
                });
            }
        }
        for (const decision of this.#db
            .prepare(permissionDecisionSelectSql(""))
            .all()
            .map(mapPermissionDecision)
            .filter(isPermissionSessionDecisionActivity)) {
            this.appendPermissionDecisionActivityEvent(decision, {
                eventId: `pact_backfill_decision_recorded_${decision.id}`,
                kind: "session_decision_recorded",
                activityAt: decision.createdAt,
                status: "active",
            });
            if (decision.revokedAt) {
                this.appendPermissionDecisionActivityEvent(decision, {
                    eventId: `pact_backfill_decision_revoked_${decision.id}`,
                    kind: "session_decision_revoked",
                    activityAt: decision.revokedAt,
                    status: "revoked",
                    revokeReason: decision.revokeReason,
                    revokeSource: decision.revokeSource,
                });
            }
        }
    }
}
