// Observed connector actions — the audit/consent/metrics hook points (Epic #750, Issue #760).
//
// PURELY ADDITIVE over #751/#753/#758/#759: this module does NOT change the connector, snapshot
// builder, re-snapshot, or token-store behaviour. It composes them with the three governance
// concerns so the future route tier (#756) calls ONE function per action and gets:
//   - consent gating before the FIRST fetch (connect / snapshot / resnapshot),
//   - an audit entry on success AND on a coded failure (re-raising the original error),
//   - operational metrics on a successful snapshot / re-snapshot.
//
// The scopeRef is derived from the connector's token-free provenance, so no board id / link ever
// reaches the audit or metrics. The actual board fetch is delegated to the injected action; this
// wrapper only observes it.
import { FigmaConnectorError } from "./figmaConnectorErrors.js";
import { appendFigmaConnectorAudit, } from "./figmaConnectorAudit.js";
import { assertReadOnlyConsent } from "./figmaConsent.js";
import { computeFigmaConnectorMetrics, } from "./figmaConnectorMetrics.js";
import { deriveFigmaScopeRef } from "./figmaScopeRef.js";
const auditFailureAndRethrow = (ctx, scopeRef, action, error) => {
    const errorCode = error instanceof FigmaConnectorError ? error.code : "FIGMA_INTERNAL";
    appendFigmaConnectorAudit({
        scopeRef,
        evidenceDir: ctx.evidenceDir,
        action,
        outcome: "error",
        errorCode,
        now: ctx.now,
    });
    throw error;
};
const countsOf = (ir, snapshot) => ({
    screens: ir.screens.length,
    renders: snapshot.screens.length,
    skipped: snapshot.skippedScreens.length,
    designTokens: ir.tokens.colors.length +
        ir.tokens.typography.length +
        ir.tokens.spacing.length +
        ir.tokens.radius.length,
    ...(ir.links.length > 0 ? { navTransitions: ir.links.length } : {}),
});
/**
 * Observe a snapshot or re-snapshot: gate on consent, run the build, then audit (with counts) and
 * compute metrics on success, or audit the coded failure and re-raise. The build itself is injected
 * so no #753/#759 behaviour changes.
 */
export const observeFigmaSnapshot = async (input) => {
    const scopeRef = deriveFigmaScopeRef(input.provenance.fileKey, input.provenance.nodeId);
    const action = input.isResnapshot === true ? "resnapshot" : "snapshot";
    // Consent is a precondition checked BEFORE the action starts, so a consent-denied attempt throws
    // here and produces no audit entry: it is a refused precondition, not a connector-action outcome.
    assertReadOnlyConsent(scopeRef, input.ctx.evidenceDir);
    let snapshot;
    try {
        snapshot = await input.run();
    }
    catch (error) {
        return auditFailureAndRethrow(input.ctx, scopeRef, action, error);
    }
    const metrics = computeFigmaConnectorMetrics(input.ir, snapshot, input.augmentation, input.extras ?? {});
    appendFigmaConnectorAudit({
        scopeRef,
        evidenceDir: input.ctx.evidenceDir,
        action,
        outcome: "ok",
        counts: countsOf(input.ir, snapshot),
        now: input.ctx.now,
    });
    return { snapshot, metrics, scopeRef };
};
/**
 * Observe a revoke (key removal): run it, then audit the action. Revoke needs no consent gate — it
 * removes access — and produces no metrics. A failure is audited then re-raised.
 */
export const observeFigmaRevoke = (input) => {
    try {
        input.run();
    }
    catch (error) {
        auditFailureAndRethrow(input.ctx, input.scopeRef, "revoke", error);
    }
    appendFigmaConnectorAudit({
        scopeRef: input.scopeRef,
        evidenceDir: input.ctx.evidenceDir,
        action: "revoke",
        outcome: "ok",
        now: input.ctx.now,
    });
};
