/**
 * src/autonomy/trust.ts
 *
 * Graduated trust — the layer that lets Moasis EARN more autonomy instead of
 * being hard-coded at a fixed boldness. The principle from the north-star: it
 * can widen what it does on its own, never its constraints. So:
 *
 *   • When the owner keeps NOT correcting its decisions (correction-rate stays
 *     low over a window of enough decisions), a small, curated set of low-risk
 *     policies is promoted ask → auto. It acts more, asks less.
 *   • When the owner starts correcting it (rate spikes), those same policies are
 *     demoted auto → ask. Trust contracts as fast as it expanded.
 *
 * HARD INVARIANT: the dangerous categories (money, contracts, data-deletion,
 * mass cold-blast — HARD_GATED_KEYS — plus cold first-touch) are NEVER
 * promotable here, no matter how trusted. Graduated trust moves the dial inside
 * the safe zone only. That's what keeps it Vision, not Ultron.
 *
 * Every change is journalled + the owner is told, and is one-click reversible
 * from the dashboard. Runs at the tail of the daily reflection cycle.
 */
import { createChildLogger } from '../utils/logger.js';
import { listPolicies, upsertPolicy, HARD_GATED_KEYS } from './policies.js';
import { computeCorrectionRate } from './reflection.js';
import { record as journalRecord } from './journal.js';
import { getFact } from './identity.js';
import { deliverMessage } from '../delivery/delivery.js';
const log = createChildLogger('autonomy:trust');
/**
 * The ONLY policies graduated trust may flip to auto. Low-risk, reversible,
 * customer-or-owner-facing nudges. Deliberately excludes cold outreach and
 * anything hard-gated.
 */
const SAFE_PROMOTABLE = [
    'proactive_followup_silent', // re-engage a KNOWN silent lead
    'proactive_invoice_dunning', // polite reminder on an overdue invoice
    'proactive_birthday_greeting', // a greeting to a known contact
    'initiative_content_publish', // publish content the owner has been approving
];
/** Categories that can NEVER be auto-promoted by trust, regardless of score. */
const NEVER_PROMOTE = new Set([...HARD_GATED_KEYS, 'initiative_cold_outreach', 'initiative_warm_outreach']);
const DEFAULTS = {
    windowDays: 14,
    minDecisions: 20,
    promoteRate: 0.05,
    demoteRate: 0.25,
    maxPromotionsPerReview: 2,
};
export async function reviewTrust(tenantId, opts = {}) {
    const cfg = { ...DEFAULTS, ...opts };
    const cr = await computeCorrectionRate(tenantId, cfg.windowDays);
    const result = { correctionRate: cr.rate, decisions: cr.decisions, promoted: [], demoted: [] };
    const policies = await listPolicies(tenantId).catch(() => []);
    const byKey = new Map(policies.map((p) => [p.policy_key, p]));
    // ── DEMOTE first (safety before boldness): corrections spiked → pull back. ──
    if (cr.rate >= cfg.demoteRate) {
        for (const key of SAFE_PROMOTABLE) {
            const p = byKey.get(key);
            if (p && p.mode === 'auto') {
                await upsertPolicy(tenantId, { policy_key: key, mode: 'ask' }).catch(() => undefined);
                result.demoted.push({ policy_key: key, from: 'auto', to: 'ask', reason: `correction-rate ${(cr.rate * 100).toFixed(0)}% ≥ ${cfg.demoteRate * 100}%` });
            }
        }
    }
    // ── PROMOTE: enough decisions + low correction-rate → earn more autonomy. ──
    if (cr.decisions >= cfg.minDecisions && cr.rate <= cfg.promoteRate && result.demoted.length === 0) {
        let promotions = 0;
        for (const key of SAFE_PROMOTABLE) {
            if (promotions >= cfg.maxPromotionsPerReview)
                break;
            if (NEVER_PROMOTE.has(key))
                continue; // belt-and-braces
            const p = byKey.get(key);
            // Promote only an existing 'ask' policy (don't create new auto powers).
            if (p && p.mode === 'ask') {
                await upsertPolicy(tenantId, { policy_key: key, mode: 'auto' }).catch(() => undefined);
                result.promoted.push({ policy_key: key, from: 'ask', to: 'auto', reason: `${cr.decisions} decisions, ${(cr.rate * 100).toFixed(0)}% corrected` });
                promotions++;
            }
        }
    }
    const changes = [...result.promoted, ...result.demoted];
    if (changes.length > 0) {
        await journalRecord({
            tenantId,
            kind: 'trust_review',
            actor: 'trust',
            action: result.promoted.length ? 'promote_autonomy' : 'demote_autonomy',
            outcome: 'success',
            reason: `correction-rate ${(cr.rate * 100).toFixed(1)}% over ${cr.decisions} decisions`,
            metadata: { promoted: result.promoted, demoted: result.demoted },
        }).catch(() => undefined);
        // Tell the owner — autonomy changes must never be silent.
        try {
            const ownerPhone = (await getFact(tenantId, 'owner.whatsapp'))?.fact_value;
            if (ownerPhone) {
                const lines = [];
                if (result.promoted.length) {
                    lines.push(`🔓 لاحظت إنك ما صححت قراراتي (نسبة التصحيح ${(cr.rate * 100).toFixed(0)}% على ${cr.decisions} قرار)، فرفعت صلاحياتي لأتصرّف تلقائياً في:`);
                    for (const c of result.promoted)
                        lines.push(`• ${LABELS[c.policy_key] ?? c.policy_key}`);
                }
                if (result.demoted.length) {
                    lines.push(`🔒 لاحظت إنك صححت قراراتي أكثر مؤخراً، فرجّعت هذي للموافقة المسبقة:`);
                    for (const c of result.demoted)
                        lines.push(`• ${LABELS[c.policy_key] ?? c.policy_key}`);
                }
                lines.push('', 'تقدر تعدّل أي صلاحية وقت ما تبي من لوحة العمليات.');
                await deliverMessage({ tenantId, to: ownerPhone, channel: 'whatsapp', message: lines.join('\n'), metadata: { audience: 'owner' } }).catch(() => undefined);
            }
        }
        catch { /* notify failure is non-fatal */ }
        log.info({ tenantId, promoted: result.promoted.length, demoted: result.demoted.length, rate: cr.rate }, 'trust review applied changes');
    }
    else {
        log.info({ tenantId, rate: cr.rate, decisions: cr.decisions }, 'trust review — no change');
    }
    return result;
}
const LABELS = {
    proactive_followup_silent: 'متابعة العملاء الساكنين',
    proactive_invoice_dunning: 'تذكير الفواتير المتأخرة',
    proactive_birthday_greeting: 'تهنئة المناسبات',
    initiative_content_publish: 'نشر المحتوى',
};
//# sourceMappingURL=trust.js.map