/**
 * Read-only projection service for proof cards.
 *
 * Composes runs + run_proof_evidence + review_queue to derive a finite,
 * sanitized card per run with evidence.
 */
import { REVIEW_PROJECTION_DECISIONS, } from "./reviewProjectionContract.js";
import { PROOF_CARD_PROJECTION_PUBLIC_SOURCE, readProofCardLabel, toneForProofCardStatus, } from "./proofCardProjectionContract.js";
import { isUnsafePublicString } from "./runProjectionContract.js";
const DECISIONS = new Set(REVIEW_PROJECTION_DECISIONS);
function deriveStatus(warningCount, hasEvidence, decision) {
    if (decision === "revoke")
        return "blocked";
    if (!hasEvidence)
        return "in-progress";
    if (warningCount > 0)
        return "needs-attention";
    return "ready";
}
export class ProofCardProjectionService {
    store;
    constructor(store) {
        this.store = store;
    }
    listProofCards(workspaceId) {
        const generatedAt = new Date().toISOString();
        const items = [];
        const runs = this.store.listRunsForWorkspace(workspaceId, 100);
        const reviews = this.store.listReviewQueue(workspaceId);
        const decisions = this.store.listReviewDecisions(workspaceId);
        const reviewByRunId = new Map(reviews.filter((r) => r.runId).map((r) => [r.runId, r]));
        const decisionByReviewId = new Map();
        for (const d of decisions) {
            if (!decisionByReviewId.has(d.reviewId))
                decisionByReviewId.set(d.reviewId, d);
        }
        for (const run of runs) {
            if (isUnsafePublicString(run.id) || isUnsafePublicString(run.workspaceId))
                continue;
            const evidence = this.store.findRunEvidence(run.id);
            const headline = readProofCardLabel(run.title ?? `Run ${run.id}`);
            if (!headline)
                continue;
            const warningCount = evidence?.warnings.length ?? 0;
            const changedFileCount = evidence?.changedFiles.length ?? 0;
            const review = reviewByRunId.get(run.id);
            const decision = review ? decisionByReviewId.get(review.id) : undefined;
            const mappedDecision = decision && DECISIONS.has(decision.decision)
                ? decision.decision
                : null;
            const reviewDecidedAt = decision?.decidedAt ?? null;
            const status = deriveStatus(warningCount, Boolean(evidence), mappedDecision);
            items.push({
                source: PROOF_CARD_PROJECTION_PUBLIC_SOURCE,
                id: run.id,
                workspaceId: run.workspaceId,
                status,
                tone: toneForProofCardStatus(status),
                headline,
                summaryLabel: review ? readProofCardLabel(review.summaryLabel ?? null) : null,
                changedFileCount,
                warningCount,
                reviewDecision: mappedDecision,
                reviewDecidedAt,
            });
        }
        return { contractVersion: "v1", generatedAt, items };
    }
}
