import { randomUUID } from 'crypto';
import * as fs from 'fs';
import * as path from 'path';
import { nexusEventBus } from './event-bus.js';
import { resolveNexusStateDir } from './runtime-registry.js';
export const SECRET_PATTERNS = [
    /sk-[a-zA-Z0-9]{16,}/i,
    /sk-ant-[a-zA-Z0-9\-_]{20,}/i,
    /AKIA[0-9A-Z]{16}/,
    /aws[_-]?secret[_-]?access[_-]?key\s*[:=]\s*[^\s"']{16,}/i,
    /ghp_[a-zA-Z0-9]{36}/,
    /github_pat_[a-zA-Z0-9_]{36,}/,
    /gho_[a-zA-Z0-9]{36}/,
    /xox[baprs]-[a-zA-Z0-9-]{10,}/i,
    /sk_live_[a-zA-Z0-9]{20,}/,
    /sk_test_[a-zA-Z0-9]{20,}/,
    /Bearer\s+[a-zA-Z0-9\-._~+/]{20,}/i,
    /[A-Z][A-Z0-9_]{2,}_(KEY|SECRET|TOKEN|PASSWORD|PASSWD|PWD|CREDENTIAL)\s*=\s*[^\s#]{8,}/,
    /api[_-]?key\s*[:=]\s*["']?[a-zA-Z0-9_-]{16,}/i,
];
const CLAIM_PATTERNS = [
    /guaranteed/i,
    /100%\s*(roi|secure|accurate|success)/i,
    /always works/i,
    /fully compliant/i,
    /proven growth/i,
];
const DANGEROUS_COMMAND_PATTERNS = [
    /\brm\s+-rf\b/i,
    /\bsudo\b/i,
    /curl\s+[^|]+\|\s*(sh|bash)/i,
    /\bchmod\s+777\b/i,
];
export class SecurityShield {
    evaluate(input) {
        const policy = input.policy ?? 'balanced';
        const findings = [];
        const text = Array.isArray(input.text) ? input.text.join('\n') : (input.text ?? '');
        const domains = new Set((input.domains ?? []).map((domain) => domain.toLowerCase()));
        if (text && SECRET_PATTERNS.some((pattern) => pattern.test(text))) {
            findings.push({
                id: 'secret-leak',
                severity: 'high',
                message: 'Potential secret or credential leak detected.',
                evidence: ['matched secret-like token in artifact content'],
            });
        }
        if (text &&
            CLAIM_PATTERNS.some((pattern) => pattern.test(text)) &&
            !input.verified &&
            [...domains].some((domain) => ['gtm', 'marketing', 'sales', 'finance', 'economics', 'writing'].includes(domain))) {
            findings.push({
                id: 'unsupported-claim',
                severity: 'medium',
                message: 'Potential unsupported commercial or editorial claim detected.',
                evidence: ['claim language found without verification evidence'],
            });
        }
        for (const binding of input.bindings ?? []) {
            if (binding.type === 'run_command' && binding.command && DANGEROUS_COMMAND_PATTERNS.some((pattern) => pattern.test(binding.command))) {
                findings.push({
                    id: 'dangerous-binding',
                    severity: 'high',
                    message: 'Dangerous command binding detected.',
                    evidence: [binding.command],
                });
            }
        }
        for (const connector of input.connectors ?? []) {
            if (SECRET_PATTERNS.some((pattern) => pattern.test(connector.target))) {
                findings.push({
                    id: 'suspicious-connector',
                    severity: 'high',
                    message: 'Connector target appears to contain a secret or credential.',
                    evidence: [connector.target],
                });
            }
        }
        if (input.stage === 'promotion' && input.riskClass === 'mutate' && !input.verified) {
            findings.push({
                id: 'mutate-promotion-unverified',
                severity: 'medium',
                message: 'Mutating artifact cannot be promoted without verified evidence.',
                evidence: ['promotion requested for mutate artifact without verified=true'],
            });
        }
        const action = resolveAction(policy, findings);
        return {
            decisionId: `shield_${randomUUID().slice(0, 8)}`,
            policy,
            stage: input.stage,
            target: input.target,
            action,
            blocked: action === 'block',
            findings,
            summary: summarizeDecision(input.target, action, findings),
        };
    }
}
const SEALED_MODE = (process.env.NEXUS_SEALED_MODE ?? '0') === '1';
function resolveAction(policy, findings) {
    if (findings.some((finding) => finding.severity === 'high'))
        return 'block';
    if (findings.some((finding) => finding.severity === 'medium')) {
        // In sealed mode, escalate medium findings to block regardless of policy
        if (SEALED_MODE)
            return 'block';
        return policy === 'permissive' ? 'warn' : 'quarantine';
    }
    if (findings.some((finding) => finding.severity === 'low'))
        return 'warn';
    return 'allow';
}
function summarizeDecision(target, action, findings) {
    if (findings.length === 0) {
        return `Security Shield allowed ${target} with no findings.`;
    }
    return `Security Shield set ${action} for ${target} with ${findings.length} finding(s).`;
}
export class SecurityAuditLog {
    auditPath;
    lastDecisionAt = null;
    counts = { total: 0, blocked: 0, byAction: {}, bySeverity: {} };
    constructor(stateDir) {
        this.auditPath = path.join(stateDir ?? resolveNexusStateDir(), 'audit.jsonl');
        // Subscribe to shield decisions via event bus — no direct coupling to phantom runtime
        nexusEventBus.on('shield.decision', (data) => {
            this.appendDecision(data).catch(() => { });
        });
    }
    async appendDecision(data) {
        const entry = JSON.stringify({ ts: Date.now(), ...data }) + '\n';
        await fs.promises.appendFile(this.auditPath, entry, 'utf8');
        this.lastDecisionAt = Date.now();
        this.counts.total++;
        if (data.blocked)
            this.counts.blocked++;
        this.counts.byAction[data.action] = (this.counts.byAction[data.action] ?? 0) + 1;
    }
    getSummary() {
        return {
            totalDecisions: this.counts.total,
            byAction: {
                allow: this.counts.byAction['allow'] ?? 0,
                warn: this.counts.byAction['warn'] ?? 0,
                quarantine: this.counts.byAction['quarantine'] ?? 0,
                block: this.counts.byAction['block'] ?? 0,
            },
            bySeverity: {
                low: this.counts.bySeverity['low'] ?? 0,
                medium: this.counts.bySeverity['medium'] ?? 0,
                high: this.counts.bySeverity['high'] ?? 0,
            },
            blocked: this.counts.blocked,
            sealedMode: SEALED_MODE,
            lastDecisionAt: this.lastDecisionAt,
        };
    }
}
let _sharedAuditLog;
export function getSharedAuditLog() {
    if (!_sharedAuditLog)
        _sharedAuditLog = new SecurityAuditLog();
    return _sharedAuditLog;
}
export const createSecurityShield = () => new SecurityShield();
