/**
 * Read-only projection service for runs.
 *
 * Wraps LocalSqliteStore queries and returns the finite public contracts
 * defined in runProjectionContract.ts. No mutations, no raw provider/path
 * exposure, and every projected field flows through redaction helpers.
 */
import { formatRuntimeTimelineRecord, } from "./runtimeTimelineContract.js";
import { RUN_PROJECTION_PUBLIC_SOURCE, readPublicTitle, toneForRunStatus, } from "./runProjectionContract.js";
const SEED_RUN_ID_PREFIX = "run-seed-";
function classifySourceMarker(id) {
    return id.startsWith(SEED_RUN_ID_PREFIX)
        ? "local-sqlite-seed"
        : "local-sqlite-live";
}
function projectRunRow(row) {
    const status = row.status;
    return {
        source: RUN_PROJECTION_PUBLIC_SOURCE,
        id: row.id,
        workspaceId: row.workspaceId,
        status,
        tone: toneForRunStatus(status),
        title: readPublicTitle(row.title ?? null),
        createdAt: row.createdAt,
        updatedAt: row.updatedAt,
        branchName: readPublicTitle(row.branchName ?? null),
        sourceMarker: classifySourceMarker(row.id),
    };
}
function projectEvidence(row) {
    if (!row)
        return null;
    const statuses = row.validations.map((validation) => validation.status);
    return {
        changedFileCount: row.changedFiles.length,
        validationStatuses: statuses,
        warningCount: row.warnings.length,
        hasFailedValidation: statuses.includes("failed"),
    };
}
export class RunProjectionService {
    store;
    constructor(store) {
        this.store = store;
    }
    listForWorkspace(workspaceId) {
        if (!workspaceId) {
            return {
                contractVersion: "v1",
                generatedAt: new Date().toISOString(),
                items: [],
            };
        }
        const rows = this.store.listRunsForWorkspace(workspaceId, 100);
        return {
            contractVersion: "v1",
            generatedAt: new Date().toISOString(),
            items: rows.map(projectRunRow),
        };
    }
    detail(runId) {
        if (!runId)
            return null;
        const run = this.store.findRun(runId);
        if (!run)
            return null;
        const base = projectRunRow(run);
        const timeline = this.buildTimeline(runId);
        const evidence = projectEvidence(this.store.findRunEvidence(runId));
        return {
            contractVersion: "v1",
            generatedAt: new Date().toISOString(),
            run: {
                ...base,
                timeline,
                evidence,
            },
        };
    }
    buildTimeline(runId) {
        const page = this.store.listRuntimeEventsForRun(runId, { limit: 100 });
        return page.events.map((event) => formatRuntimeTimelineRecord({
            id: event.id,
            source: event.source,
            type: event.type,
            occurredAt: event.occurredAt,
            title: event.title,
            description: event.description,
            tone: event.tone,
            metadata: event.metadata,
            createdAt: event.createdAt,
        }));
    }
}
