import { inferTaskPhaseKey } from "./phase-resolution.js";
export const DELIVERY_EVIDENCE_METADATA_KEY = "deliveryEvidence";
export const DELIVERY_WAIVER_METADATA_KEY = "deliveryWaiver";
export const DELIVERY_EVIDENCE_V2_MODES = new Set([
    "github-pr",
    "local-reviewed-merge",
    "direct-reviewed-merge",
    "external-review"
]);
function isRecord(value) {
    return value !== null && typeof value === "object" && !Array.isArray(value);
}
function nonEmptyString(value) {
    return typeof value === "string" && value.trim().length > 0;
}
function positiveInteger(value) {
    return typeof value === "number" && Number.isInteger(value) && value > 0;
}
function hasResultLikeField(value) {
    return (nonEmptyString(value.result) ||
        nonEmptyString(value.status) ||
        nonEmptyString(value.conclusion) ||
        typeof value.exitCode === "number");
}
function validateChecksAndValidationCommands(value, prefix) {
    const missing = [];
    const checks = value.checks;
    if (!Array.isArray(checks) || checks.length === 0) {
        missing.push(`${prefix}.checks`);
    }
    else {
        for (let i = 0; i < checks.length; i++) {
            const check = checks[i];
            if (!isRecord(check)) {
                missing.push(`${prefix}.checks[${i}]`);
                continue;
            }
            if (!nonEmptyString(check.name))
                missing.push(`${prefix}.checks[${i}].name`);
            if (!hasResultLikeField(check))
                missing.push(`${prefix}.checks[${i}].result`);
        }
    }
    const validationCommands = value.validationCommands;
    if (!Array.isArray(validationCommands) || validationCommands.length === 0) {
        missing.push(`${prefix}.validationCommands`);
    }
    else {
        for (let i = 0; i < validationCommands.length; i++) {
            const validation = validationCommands[i];
            if (!isRecord(validation)) {
                missing.push(`${prefix}.validationCommands[${i}]`);
                continue;
            }
            if (!nonEmptyString(validation.command)) {
                missing.push(`${prefix}.validationCommands[${i}].command`);
            }
            if (!hasResultLikeField(validation)) {
                missing.push(`${prefix}.validationCommands[${i}].result`);
            }
        }
    }
    return missing;
}
/** v1 GitHub PR-shaped evidence (legacy). */
export function missingDeliveryEvidenceFieldsV1(value) {
    if (!isRecord(value)) {
        return [DELIVERY_EVIDENCE_METADATA_KEY];
    }
    const missing = [];
    if (value.schemaVersion !== 1)
        missing.push("deliveryEvidence.schemaVersion");
    if (!nonEmptyString(value.branchName))
        missing.push("deliveryEvidence.branchName");
    if (!nonEmptyString(value.prUrl))
        missing.push("deliveryEvidence.prUrl");
    if (!positiveInteger(value.prNumber))
        missing.push("deliveryEvidence.prNumber");
    if (!nonEmptyString(value.baseBranch))
        missing.push("deliveryEvidence.baseBranch");
    if (!nonEmptyString(value.mergeSha))
        missing.push("deliveryEvidence.mergeSha");
    missing.push(...validateChecksAndValidationCommands(value, "deliveryEvidence"));
    return missing;
}
function validateDeliveryEvidenceV2(evidence, allowedEvidenceModes) {
    const prefix = "deliveryEvidence";
    if (evidence.schemaVersion !== 2) {
        return {
            ok: false,
            code: "delivery-evidence-malformed-v2",
            missingFields: [`${prefix}.schemaVersion`]
        };
    }
    const modeRaw = evidence.mode;
    if (!nonEmptyString(modeRaw)) {
        return {
            ok: false,
            code: "delivery-evidence-malformed-v2",
            missingFields: [`${prefix}.mode`]
        };
    }
    const mode = modeRaw.trim();
    if (!DELIVERY_EVIDENCE_V2_MODES.has(mode)) {
        return {
            ok: false,
            code: "delivery-evidence-unsupported-mode",
            missingFields: [`${prefix}.mode`]
        };
    }
    const modeTyped = mode;
    if (allowedEvidenceModes && allowedEvidenceModes.length > 0 && !allowedEvidenceModes.includes(modeTyped)) {
        return {
            ok: false,
            code: "delivery-evidence-mode-not-allowed",
            missingFields: [`${prefix}.mode`]
        };
    }
    const missing = [];
    if (!nonEmptyString(evidence.branchName))
        missing.push(`${prefix}.branchName`);
    if (!nonEmptyString(evidence.baseBranch))
        missing.push(`${prefix}.baseBranch`);
    if (!nonEmptyString(evidence.mergeSha))
        missing.push(`${prefix}.mergeSha`);
    missing.push(...validateChecksAndValidationCommands(evidence, prefix));
    if (modeTyped === "github-pr") {
        if (!nonEmptyString(evidence.prUrl))
            missing.push(`${prefix}.prUrl`);
        if (!positiveInteger(evidence.prNumber))
            missing.push(`${prefix}.prNumber`);
    }
    else if (modeTyped === "local-reviewed-merge" || modeTyped === "direct-reviewed-merge") {
        if (!nonEmptyString(evidence.reviewer)) {
            missing.push(`${prefix}.reviewer`);
        }
        if (modeTyped === "local-reviewed-merge") {
            if (!nonEmptyString(evidence.reviewArtifactRelativePath)) {
                missing.push(`${prefix}.reviewArtifactRelativePath`);
            }
        }
    }
    else if (modeTyped === "external-review") {
        if (!nonEmptyString(evidence.externalReviewUrl)) {
            missing.push(`${prefix}.externalReviewUrl`);
        }
    }
    if (missing.length > 0) {
        return { ok: false, code: "delivery-evidence-malformed-v2", missingFields: missing };
    }
    return null;
}
export function summarizeDeliveryEvidence(evidence) {
    if (!isRecord(evidence)) {
        return { schemaVersion: null, mode: null };
    }
    const sv = evidence.schemaVersion;
    const schemaVersion = typeof sv === "number" && Number.isInteger(sv) ? sv : null;
    const modeRaw = evidence.mode;
    const mode = nonEmptyString(modeRaw) ? modeRaw.trim() : null;
    return { schemaVersion, mode };
}
function missingDeliveryWaiverFields(value) {
    if (!isRecord(value)) {
        return [DELIVERY_WAIVER_METADATA_KEY];
    }
    const missing = [];
    if (value.schemaVersion !== 1)
        missing.push("deliveryWaiver.schemaVersion");
    if (!nonEmptyString(value.actor))
        missing.push("deliveryWaiver.actor");
    if (!nonEmptyString(value.rationale))
        missing.push("deliveryWaiver.rationale");
    if (!nonEmptyString(value.timestamp))
        missing.push("deliveryWaiver.timestamp");
    if (!nonEmptyString(value.scope))
        missing.push("deliveryWaiver.scope");
    return missing;
}
function isLocalOnlyTask(task) {
    const metadata = task.metadata;
    return Boolean(metadata?.deliveryEvidenceRequired === false ||
        metadata?.localOnly === true ||
        metadata?.nonShipping === true);
}
export function isPhaseDeliveryTask(task) {
    if (task.archived || isLocalOnlyTask(task)) {
        return false;
    }
    if (task.type === "wishlist_intake" || task.type === "transcript_churn") {
        return false;
    }
    return inferTaskPhaseKey(task) !== null;
}
function violationMessageForCode(code) {
    switch (code) {
        case "delivery-evidence-malformed-v1":
            return "deliveryEvidence (v1) is present but missing required fields.";
        case "delivery-evidence-malformed-v2":
            return "deliveryEvidence (v2) is present but missing required fields.";
        case "delivery-evidence-unsupported-schema":
            return "deliveryEvidence uses an unsupported schemaVersion (expected 1 or 2).";
        case "delivery-evidence-unsupported-mode":
            return "deliveryEvidence (v2) uses an unsupported mode.";
        case "delivery-evidence-mode-not-allowed":
            return "deliveryEvidence (v2) mode is not allowed by the active delivery policy.";
        default:
            return "deliveryEvidence validation failed.";
    }
}
function evaluateEvidenceBlob(raw, options) {
    if (!isRecord(raw)) {
        return { ok: false, code: "delivery-evidence-malformed-v1", missingFields: [DELIVERY_EVIDENCE_METADATA_KEY] };
    }
    const sv = raw.schemaVersion;
    if (sv === 1) {
        const missing = missingDeliveryEvidenceFieldsV1(raw);
        if (missing.length > 0) {
            return { ok: false, code: "delivery-evidence-malformed-v1", missingFields: missing };
        }
        return { ok: true, schemaVersion: 1, evidenceMode: "v1-github-pr" };
    }
    if (sv === 2) {
        const v2Err = validateDeliveryEvidenceV2(raw, options?.allowedEvidenceModes);
        if (v2Err) {
            return v2Err;
        }
        const modeStr = nonEmptyString(raw.mode) ? raw.mode.trim() : "";
        return {
            ok: true,
            schemaVersion: 2,
            evidenceMode: modeStr
        };
    }
    if (sv === undefined) {
        return {
            ok: false,
            code: "delivery-evidence-unsupported-schema",
            missingFields: ["deliveryEvidence.schemaVersion"]
        };
    }
    return {
        ok: false,
        code: "delivery-evidence-unsupported-schema",
        missingFields: ["deliveryEvidence.schemaVersion"]
    };
}
export function validateDeliveryEvidenceMetadata(raw, options) {
    const result = evaluateEvidenceBlob(raw, options);
    if ("ok" in result && result.ok === true) {
        return result;
    }
    const bad = result;
    return {
        ok: false,
        code: bad.code,
        message: violationMessageForCode(bad.code),
        missingFields: bad.missingFields
    };
}
function actualEvidenceMode(raw) {
    if (!isRecord(raw)) {
        return null;
    }
    if (raw.schemaVersion === 1) {
        return "v1-github-pr";
    }
    return nonEmptyString(raw.mode) ? raw.mode.trim() : null;
}
function policyViolationFields(options, actualMode) {
    return {
        requiredEvidenceMode: options?.requiredEvidenceMode ?? null,
        actualEvidenceMode: actualMode,
        policyProfile: options?.policyProfile ?? null,
        policyWarnings: options?.policyWarnings ? [...options.policyWarnings] : []
    };
}
export function evaluateDeliveryEvidence(task, options) {
    if (!isPhaseDeliveryTask(task)) {
        return {
            required: false,
            satisfied: true,
            satisfiedBy: "not-required",
            violations: []
        };
    }
    const metadata = task.metadata ?? {};
    const evidenceRaw = metadata[DELIVERY_EVIDENCE_METADATA_KEY];
    if (evidenceRaw !== undefined && evidenceRaw !== null) {
        const evaluated = evaluateEvidenceBlob(evidenceRaw, options);
        if ("ok" in evaluated && evaluated.ok === true) {
            return {
                required: true,
                satisfied: true,
                satisfiedBy: "evidence",
                violations: [],
                evidenceSchemaVersion: evaluated.schemaVersion,
                evidenceMode: evaluated.evidenceMode
            };
        }
        const bad = evaluated;
        return {
            required: true,
            satisfied: false,
            satisfiedBy: null,
            violations: [
                {
                    taskId: task.id,
                    title: task.title,
                    status: task.status,
                    phaseKey: inferTaskPhaseKey(task),
                    code: bad.code,
                    message: violationMessageForCode(bad.code),
                    missingFields: bad.missingFields,
                    ...policyViolationFields(options, actualEvidenceMode(evidenceRaw))
                }
            ]
        };
    }
    const waiverMissing = missingDeliveryWaiverFields(metadata[DELIVERY_WAIVER_METADATA_KEY]);
    if (waiverMissing.length === 0) {
        return {
            required: true,
            satisfied: true,
            satisfiedBy: "waiver",
            violations: []
        };
    }
    return {
        required: true,
        satisfied: false,
        satisfiedBy: null,
        violations: [
            {
                taskId: task.id,
                title: task.title,
                status: task.status,
                phaseKey: inferTaskPhaseKey(task),
                code: "delivery-evidence-missing",
                message: "Phase delivery completion requires metadata.deliveryEvidence or metadata.deliveryWaiver.",
                missingFields: [...[DELIVERY_EVIDENCE_METADATA_KEY], ...waiverMissing],
                ...policyViolationFields(options, null)
            }
        ]
    };
}
export function buildPhaseDeliveryPreflight(args) {
    const includeInProgress = args.includeInProgress ?? true;
    const targetPhase = args.phaseKey?.trim() || null;
    const statuses = includeInProgress ? ["completed", "in_progress"] : ["completed"];
    const checked = args.tasks.filter((task) => {
        if (!statuses.includes(task.status))
            return false;
        if (!isPhaseDeliveryTask(task))
            return false;
        if (targetPhase === null)
            return true;
        return inferTaskPhaseKey(task) === targetPhase;
    });
    const violations = checked.flatMap((task) => {
        const policyContext = args.policyContextByTaskId?.[task.id];
        const allowed = args.allowedEvidenceModesByTaskId?.[task.id];
        return evaluateDeliveryEvidence(task, policyContext ?? (allowed ? { allowedEvidenceModes: allowed } : undefined)).violations;
    });
    return {
        schemaVersion: 1,
        phaseKey: targetPhase,
        checkedTaskCount: checked.length,
        violationCount: violations.length,
        violations
    };
}
export function buildPhaseCloseoutReadiness(args) {
    const targetPhase = args.phaseKey?.trim() || null;
    const phaseTasks = args.tasks.filter((task) => {
        if (!isPhaseDeliveryTask(task))
            return false;
        if (targetPhase === null)
            return true;
        return inferTaskPhaseKey(task) === targetPhase;
    });
    const remainingByStatus = {};
    let terminalCount = 0;
    for (const task of phaseTasks) {
        if (task.status === "completed" || task.status === "cancelled") {
            terminalCount += 1;
            continue;
        }
        const rows = remainingByStatus[task.status] ?? [];
        rows.push({ id: task.id, title: task.title });
        remainingByStatus[task.status] = rows;
    }
    const remainingCount = Object.values(remainingByStatus).reduce((sum, rows) => sum + (rows?.length ?? 0), 0);
    return {
        schemaVersion: 1,
        phaseKey: targetPhase,
        passed: remainingCount === 0,
        remainingCount,
        remainingByStatus,
        terminalCount,
        checkedTaskCount: phaseTasks.length
    };
}
export function readDeliveryEvidenceEnforcementMode(effectiveConfig) {
    const tasks = effectiveConfig?.tasks;
    const tasksObj = isRecord(tasks) ? tasks : undefined;
    const deliveryEvidence = tasksObj?.deliveryEvidence;
    const deliveryObj = isRecord(deliveryEvidence) ? deliveryEvidence : undefined;
    const raw = deliveryObj?.enforcementMode;
    if (raw === "off" || raw === "advisory" || raw === "enforce") {
        return raw;
    }
    return "enforce";
}
export function createDeliveryEvidenceGuard(options = {}) {
    const enforcementMode = options.enforcementMode ?? "enforce";
    return {
        name: "delivery-evidence",
        canTransition(task, targetState, _context) {
            if (targetState !== "completed" || enforcementMode === "off") {
                return { allowed: true, guardName: "delivery-evidence" };
            }
            const policyContext = options.resolvePolicyContext?.(task);
            const evaluation = evaluateDeliveryEvidence(task, {
                allowedEvidenceModes: options.allowedEvidenceModes,
                requiredEvidenceMode: options.requiredEvidenceMode,
                policyProfile: options.policyProfile,
                policyWarnings: options.policyWarnings,
                ...policyContext
            });
            if (evaluation.satisfied) {
                const code = evaluation.satisfiedBy === "waiver"
                    ? "delivery-waiver-present"
                    : evaluation.satisfiedBy === "not-required"
                        ? "delivery-evidence-not-required"
                        : "delivery-evidence-present";
                return {
                    allowed: true,
                    guardName: "delivery-evidence",
                    code
                };
            }
            const violation = evaluation.violations[0];
            const blocking = enforcementMode === "enforce";
            return {
                allowed: !blocking,
                guardName: "delivery-evidence",
                code: blocking ? (violation?.code ?? "delivery-evidence-missing") : "delivery-evidence-advisory",
                message: violation?.message ?? "Phase delivery completion evidence is missing."
            };
        }
    };
}
