// engine/approval-workflow.ts — Approval workflow business logic (SPEC-283)
// Handles: gate check, SLA calculation, status aggregation.
// ---------------------------------------------------------------------------
// Gate check
// ---------------------------------------------------------------------------
/**
 * Verify whether a spec meets the required approval count.
 * Returns null when the gate is satisfied, or an error message when not.
 */
export function checkApprovalGate(records, policy, specId) {
    const approvals = records.filter((r) => r.specId === specId && r.action === 'approved');
    if (approvals.length >= policy.requiredApprovals) {
        return null;
    }
    return (`Spec ${specId} requires ${String(policy.requiredApprovals)} approval(s), ` +
        `currently has ${String(approvals.length)}. Use approve_spec to add approvals.`);
}
// ---------------------------------------------------------------------------
// SLA calculation
// ---------------------------------------------------------------------------
/** Milliseconds in one hour. */
const MS_PER_HOUR = 3_600_000;
/**
 * Calculate how many hours a spec has been in review.
 * reviewStartedAt should be an ISO 8601 string.
 * Returns 0 when reviewStartedAt is not provided.
 */
export function hoursInReview(reviewStartedAt) {
    if (reviewStartedAt === undefined || reviewStartedAt === '') {
        return 0;
    }
    const start = new Date(reviewStartedAt).getTime();
    if (isNaN(start)) {
        return 0;
    }
    return (Date.now() - start) / MS_PER_HOUR;
}
/**
 * Compute SLA breach status given the start of review and the policy.
 */
export function calculateSLAStatus(reviewStartedAt, policy) {
    const elapsed = hoursInReview(reviewStartedAt);
    if (policy === null || elapsed === 0) {
        return { slaBreached: false, slaBreachHours: 0, hoursInReview: elapsed };
    }
    const breach = elapsed > policy.reviewSLAHours;
    const breachHours = breach ? elapsed - policy.reviewSLAHours : 0;
    return {
        slaBreached: breach,
        slaBreachHours: Math.round(breachHours * 100) / 100,
        hoursInReview: Math.round(elapsed * 100) / 100,
    };
}
// ---------------------------------------------------------------------------
// Status aggregation
// ---------------------------------------------------------------------------
/**
 * Build the full ApprovalStatus for a spec.
 */
export function buildApprovalStatus(specId, records, policy, reviewStartedAt) {
    const approvals = records.filter((r) => r.specId === specId && r.action === 'approved');
    const approvalCount = approvals.length;
    const required = policy?.requiredApprovals ?? 0;
    const approvalsNeeded = Math.max(0, required - approvalCount);
    const gateOpen = policy === null || approvalCount >= required;
    const approvers = approvals.map((r) => r.reviewerIdentity.displayName);
    const sla = calculateSLAStatus(reviewStartedAt, policy);
    return {
        specId,
        approvalCount,
        approvalsNeeded,
        gateOpen,
        approvers,
        records,
        slaBreached: sla.slaBreached,
        slaBreachHours: sla.slaBreachHours,
        hoursInReview: sla.hoursInReview,
        reviewStartedAt,
        policy: policy ?? undefined,
    };
}
//# sourceMappingURL=approval-workflow.js.map