/**
 * OPA/Rego Policy Evaluation
 *
 * Integrates with Open Policy Agent for policy-as-code evaluation.
 * Supports both local OPA bundles and remote OPA servers.
 *
 * @module enterprise/policy/opa
 */
import { readFile, access, constants } from "fs/promises";
// ============================================================================
// OPA Client
// ============================================================================
/**
 * OPA policy evaluator
 */
export class OPAClient {
    config;
    constructor(config) {
        this.config = {
            decisionPath: "vaspera/certification/allow",
            timeout: 5000,
            ...config,
        };
    }
    /**
     * Evaluate policy
     */
    async evaluate(input) {
        if (this.config.mode === "remote") {
            return this.evaluateRemote(input);
        }
        else {
            return this.evaluateLocal(input);
        }
    }
    /**
     * Evaluate policy remotely via OPA server
     */
    async evaluateRemote(input) {
        if (!this.config.serverUrl) {
            throw new Error("OPA server URL required for remote evaluation");
        }
        const url = new URL(`/v1/data/${this.config.decisionPath?.replace(/\./g, "/")}`, this.config.serverUrl);
        const controller = new AbortController();
        const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
        try {
            const response = await fetch(url.toString(), {
                method: "POST",
                headers: {
                    "Content-Type": "application/json",
                    ...this.config.headers,
                },
                body: JSON.stringify({ input }),
                signal: controller.signal,
            });
            clearTimeout(timeoutId);
            if (!response.ok) {
                throw new Error(`OPA server returned ${response.status}`);
            }
            const data = (await response.json());
            return this.parseResult(data.result);
        }
        catch (error) {
            clearTimeout(timeoutId);
            if (error instanceof Error && error.name === "AbortError") {
                throw new Error("OPA evaluation timed out");
            }
            throw error;
        }
    }
    /**
     * Evaluate policy locally using bundled policies
     */
    async evaluateLocal(input) {
        if (!this.config.bundlePath) {
            throw new Error("Bundle path required for local evaluation");
        }
        // Check if bundle exists
        try {
            await access(this.config.bundlePath, constants.R_OK);
        }
        catch {
            throw new Error(`Policy bundle not found: ${this.config.bundlePath}`);
        }
        // Load policy bundle
        const policyContent = await readFile(this.config.bundlePath, "utf-8");
        // For now, use JSON-based policy evaluation
        // In production, would use opa-wasm or spawn OPA process
        const policy = JSON.parse(policyContent);
        return this.evaluatePolicy(policy, input);
    }
    /**
     * Evaluate JSON policy bundle
     */
    evaluatePolicy(policy, input) {
        const { certification } = input;
        const denialReasons = [];
        const warnings = [];
        const requiredRemediations = [];
        const complianceFindings = [];
        // Evaluate score threshold
        if (policy.rules.minScore && certification.score < policy.rules.minScore) {
            denialReasons.push(`Certification score ${certification.score} below minimum ${policy.rules.minScore}`);
        }
        // Evaluate severity thresholds
        if (policy.rules.maxCriticalFindings !== undefined) {
            const critical = certification.findings.filter((f) => f.severity === "critical").length;
            if (critical > policy.rules.maxCriticalFindings) {
                denialReasons.push(`${critical} critical findings exceed maximum of ${policy.rules.maxCriticalFindings}`);
            }
        }
        if (policy.rules.maxHighFindings !== undefined) {
            const high = certification.findings.filter((f) => f.severity === "high").length;
            if (high > policy.rules.maxHighFindings) {
                denialReasons.push(`${high} high-severity findings exceed maximum of ${policy.rules.maxHighFindings}`);
            }
        }
        // Evaluate required frameworks
        if (policy.rules.requiredFrameworks) {
            for (const framework of policy.rules.requiredFrameworks) {
                if (!certification.frameworks.includes(framework)) {
                    denialReasons.push(`Required framework ${framework} not included in certification`);
                }
            }
        }
        // Evaluate blocked categories
        if (policy.rules.blockedCategories) {
            for (const finding of certification.findings) {
                if (policy.rules.blockedCategories.includes(finding.category)) {
                    denialReasons.push(`Finding in blocked category: ${finding.category} (${finding.id})`);
                }
            }
        }
        // Evaluate required remediations
        if (policy.rules.requireRemediationFor) {
            for (const finding of certification.findings) {
                if (policy.rules.requireRemediationFor.includes(finding.severity)) {
                    requiredRemediations.push(`Remediation required for ${finding.severity} finding: ${finding.id}`);
                }
            }
        }
        // Add warnings for conditions that don't block
        if (policy.rules.warnOnCategories) {
            for (const finding of certification.findings) {
                if (policy.rules.warnOnCategories.includes(finding.category)) {
                    warnings.push(`Warning for category ${finding.category}: ${finding.id}`);
                }
            }
        }
        // Evaluate compliance controls
        if (policy.complianceChecks) {
            for (const check of policy.complianceChecks) {
                const passed = this.evaluateComplianceCheck(check, input);
                complianceFindings.push({
                    control: check.control,
                    framework: check.framework,
                    status: passed ? "pass" : "fail",
                    message: check.message,
                });
                if (!passed && check.blocking) {
                    denialReasons.push(`Compliance check failed: ${check.control}`);
                }
            }
        }
        const allow = denialReasons.length === 0;
        return {
            allow,
            denialReasons: denialReasons.length > 0 ? denialReasons : undefined,
            warnings: warnings.length > 0 ? warnings : undefined,
            requiredRemediations: requiredRemediations.length > 0 ? requiredRemediations : undefined,
            complianceFindings: complianceFindings.length > 0 ? complianceFindings : undefined,
            evaluatedAt: new Date().toISOString(),
            policyVersion: policy.version,
        };
    }
    /**
     * Evaluate a single compliance check
     */
    evaluateComplianceCheck(check, input) {
        const { certification } = input;
        switch (check.type) {
            case "no_findings":
                return !certification.findings.some((f) => f.category === check.category);
            case "max_findings":
                return (certification.findings.filter((f) => f.category === check.category)
                    .length <= (check.maxCount || 0));
            case "min_score":
                return certification.score >= (check.minScore || 0);
            case "has_framework":
                return certification.frameworks.includes(check.requiredFramework || "");
            default:
                return true;
        }
    }
    /**
     * Parse OPA result into PolicyResult
     */
    parseResult(result) {
        if (!result) {
            return {
                allow: false,
                denialReasons: ["No policy result returned"],
                evaluatedAt: new Date().toISOString(),
            };
        }
        return {
            allow: result.allow === true,
            denialReasons: result.deny_reasons,
            warnings: result.warnings,
            requiredRemediations: result.required_remediations,
            complianceFindings: result.compliance_findings,
            rawResult: result,
            evaluatedAt: new Date().toISOString(),
            policyVersion: result.policy_version,
        };
    }
    /**
     * Check if OPA is available
     */
    async isAvailable() {
        if (this.config.mode === "remote") {
            try {
                const response = await fetch(`${this.config.serverUrl}/health`, {
                    method: "GET",
                    signal: AbortSignal.timeout(2000),
                });
                return response.ok;
            }
            catch {
                return false;
            }
        }
        else {
            if (!this.config.bundlePath) {
                return false;
            }
            try {
                await access(this.config.bundlePath, constants.R_OK);
                return true;
            }
            catch {
                return false;
            }
        }
    }
}
// ============================================================================
// Policy Templates
// ============================================================================
/**
 * Generate default policy bundle
 */
export function generateDefaultPolicy() {
    return {
        version: "1.0.0",
        rules: {
            minScore: 70,
            maxCriticalFindings: 0,
            maxHighFindings: 3,
            requireRemediationFor: ["critical"],
            warnOnCategories: ["dependency-vuln"],
        },
        complianceChecks: [
            {
                control: "SEC-001",
                framework: "internal",
                message: "No critical vulnerabilities allowed",
                blocking: true,
                type: "no_findings",
                category: "vulnerability",
            },
            {
                control: "SEC-002",
                framework: "internal",
                message: "Minimum certification score of 70%",
                blocking: true,
                type: "min_score",
                minScore: 70,
            },
        ],
    };
}
/**
 * Generate SOC 2 policy bundle
 */
export function generateSOC2Policy() {
    return {
        version: "1.0.0-soc2",
        rules: {
            minScore: 85,
            maxCriticalFindings: 0,
            maxHighFindings: 0,
            requiredFrameworks: ["SOC2"],
            requireRemediationFor: ["critical", "high"],
        },
        complianceChecks: [
            {
                control: "CC6.1",
                framework: "SOC2",
                message: "Logical access controls implemented",
                blocking: true,
                type: "no_findings",
                category: "access-control",
            },
            {
                control: "CC7.1",
                framework: "SOC2",
                message: "System operations monitored",
                blocking: true,
                type: "has_framework",
                requiredFramework: "SOC2",
            },
        ],
    };
}
// ============================================================================
// Integration with Certification
// ============================================================================
/**
 * Convert policy result to findings
 */
export function policyResultToFindings(result) {
    const findings = [];
    // Convert denial reasons to critical findings
    if (result.denialReasons) {
        for (const reason of result.denialReasons) {
            findings.push({
                id: `policy-${findings.length + 1}`,
                severity: "critical",
                category: "policy-violation",
                description: `Policy violation: ${reason}`,
                evidence: `Policy version: ${result.policyVersion || "unknown"}`,
                confidence: 100,
                verifications: [],
                created_at: result.evaluatedAt,
                scanner_source: undefined,
            });
        }
    }
    // Convert warnings to low findings
    if (result.warnings) {
        for (const warning of result.warnings) {
            findings.push({
                id: `policy-warn-${findings.length + 1}`,
                severity: "low",
                category: "policy-warning",
                description: `Policy warning: ${warning}`,
                evidence: `Policy version: ${result.policyVersion || "unknown"}`,
                confidence: 100,
                verifications: [],
                created_at: result.evaluatedAt,
                scanner_source: undefined,
            });
        }
    }
    return findings;
}
// ============================================================================
// Factory Functions
// ============================================================================
/**
 * Create OPA client
 */
export function createOPAClient(config) {
    return new OPAClient(config);
}
/**
 * Create local OPA client from bundle
 */
export function createLocalOPAClient(bundlePath) {
    return new OPAClient({
        mode: "local",
        bundlePath,
    });
}
/**
 * Create remote OPA client
 */
export function createRemoteOPAClient(serverUrl) {
    return new OPAClient({
        mode: "remote",
        serverUrl,
    });
}
//# sourceMappingURL=opa.js.map