/**
 * src/insights/roi.ts
 *
 * The "Muassis paid for itself" engine. Computes two killer numbers per
 * tenant that surface front-and-center on the dashboard:
 *
 *   1. hours_saved_total — sum across every tool execution Muassis has
 *      done, weighted by an estimate of how long the equivalent manual
 *      task would have taken a human. Conservative estimates by design;
 *      we'd rather understate than overpromise.
 *
 *   2. revenue_attributed — sum of business value Muassis directly drove:
 *        - quotes sent (× conversion estimate)
 *        - invoices issued (full value)
 *        - leads qualified (× lead-value estimate)
 *        - customers contacted that became active
 *
 * Both update LIVE as journal entries land. The dashboard polls every
 * 30s. Watching the counter tick up is the engagement loop — the owner
 * sees their AI hire literally printing money.
 */
import { supabase } from '../utils/supabase.js';
import { createChildLogger } from '../utils/logger.js';
const log = createChildLogger('insights:roi');
// Per-tool time savings, conservative. The unit is MINUTES of human
// effort saved per execution. Numbers are calibrated against typical SMB
// task durations in Saudi Arabia — drafting a sales email by hand takes
// 5-8 min, hand-writing an invoice takes 4-6 min, scrolling sites for
// research takes 10-20 min, etc.
const TIME_SAVED_MINUTES_PER_TOOL = {
    // Communications (each one saves a real-time message to draft + send)
    send_email: 6,
    send_whatsapp: 3,
    send_telegram: 3,
    deliver_message: 3,
    schedule_meeting: 5,
    // Research / answers (each replaces 10-30 min of manual search)
    web_search: 12,
    web_fetch: 8,
    research_topic: 25,
    internal_knowledge_search: 8,
    verify_answer: 6,
    recall_fact: 4,
    recall_conversation: 6,
    lookup_skill: 3,
    // Documents (each is 10-60 min of manual drafting)
    create_pdf_report: 45,
    create_excel: 30,
    create_presentation: 50,
    html_to_pdf: 15,
    write_file: 8,
    read_file: 3,
    learn_from_document: 20,
    // Business workflow (each replaces a real billable hour or fraction)
    zatca_invoice: 12,
    send_quote: 15,
    issue_invoice: 18,
    log_lead: 4,
    qualify_lead: 7,
    customer_create: 5,
    customer_update: 3,
    record_insight: 5,
    remember_fact: 2,
    // Browser automation (each replaces 5-30 min of clicking)
    browser_navigate: 4,
    browser_click: 3,
    browser_fill_form: 8,
    browser_take_screenshot: 2,
    browser_snapshot: 4,
    browser_extract: 10,
    // System
    exec_command: 5,
    open_url: 1,
    play_youtube: 1,
    open_file: 2,
};
// Revenue per business action, in SAR. Conservative SMB averages.
// - send_quote: average quote value × typical SMB conversion rate
// - issue_invoice: full invoice value (sum from data when available)
// - qualify_lead: average qualified-lead-value (industry-dependent)
const REVENUE_PER_ACTION_SAR = {
    send_quote: 350, // 25% × ~1400 avg quote
    issue_invoice: 1200, // direct invoice value avg
    qualify_lead: 180, // qualified lead × close rate
    log_lead: 50, // raw lead value
    schedule_meeting: 200, // meeting → discovery conversion value
};
const DEFAULT_TIME_SAVED_MINUTES = 2; // unknown tool that still ran successfully
export async function computeRoi(tenantId) {
    // Pull all successful tool executions from the journal. We treat
    // kind='tool' entries with metadata.success !== false as productive.
    // Fall back gracefully if the tool name is in metadata.tool, action
    // text, or a different shape — the journal has accreted formats.
    const { data: entries } = await supabase
        .from('journal_entries')
        .select('action, outcome, metadata, created_at, kind')
        .eq('tenant_id', tenantId)
        .eq('kind', 'tool')
        .limit(2000); // cap so we don't OOM on a huge journal
    const rows = (entries ?? []);
    const now = Date.now();
    const dayMs = 24 * 60 * 60 * 1000;
    const monthStart = new Date();
    monthStart.setDate(1);
    monthStart.setHours(0, 0, 0, 0);
    const todayStart = new Date();
    todayStart.setHours(0, 0, 0, 0);
    let minutesAll = 0;
    let minutesMonth = 0;
    let minutesToday = 0;
    let revenueAll = 0;
    let revenueMonth = 0;
    const byTool = new Map();
    let totalActions = 0;
    const daysWithAction = new Set();
    for (const r of rows) {
        const success = r.outcome !== 'failed' && r.outcome !== 'error';
        if (!success)
            continue;
        // Tool name from action ("send_email") or metadata.tool
        const tool = (r.action || (r.metadata && (r.metadata.tool || r.metadata.tool_name)) || '').trim();
        if (!tool)
            continue;
        const mins = TIME_SAVED_MINUTES_PER_TOOL[tool] ?? DEFAULT_TIME_SAVED_MINUTES;
        const rev = REVENUE_PER_ACTION_SAR[tool] ?? 0;
        const ts = r.created_at ? new Date(r.created_at).getTime() : now;
        minutesAll += mins;
        revenueAll += rev;
        if (ts >= monthStart.getTime()) {
            minutesMonth += mins;
            revenueMonth += rev;
        }
        if (ts >= todayStart.getTime()) {
            minutesToday += mins;
        }
        const acc = byTool.get(tool) ?? { count: 0, minutes: 0 };
        acc.count += 1;
        acc.minutes += mins;
        byTool.set(tool, acc);
        totalActions += 1;
        daysWithAction.add(new Date(ts).toISOString().slice(0, 10));
    }
    // Streak: count consecutive days BACKWARDS from today (or yesterday if
    // today has no actions yet — don't punish the owner for a slow morning).
    const sortedDays = Array.from(daysWithAction).sort().reverse();
    const today = new Date(now).toISOString().slice(0, 10);
    const yesterday = new Date(now - dayMs).toISOString().slice(0, 10);
    let cursor = daysWithAction.has(today) ? new Date(now) : new Date(now - dayMs);
    if (!daysWithAction.has(today) && !daysWithAction.has(yesterday)) {
        // gap > 1 day → streak is 0
        cursor = new Date(0);
    }
    let currentStreak = 0;
    while (daysWithAction.has(cursor.toISOString().slice(0, 10))) {
        currentStreak++;
        cursor = new Date(cursor.getTime() - dayMs);
    }
    // Longest streak: scan all days for the longest consecutive run
    let longestStreak = 0;
    let run = 0;
    const days = sortedDays.slice().reverse(); // oldest first
    for (let i = 0; i < days.length; i++) {
        if (i === 0) {
            run = 1;
            longestStreak = Math.max(longestStreak, 1);
            continue;
        }
        const prev = new Date(days[i - 1] + 'T00:00:00Z').getTime();
        const cur = new Date(days[i] + 'T00:00:00Z').getTime();
        if (cur - prev === dayMs) {
            run++;
        }
        else {
            run = 1;
        }
        longestStreak = Math.max(longestStreak, run);
    }
    const topContributors = Array.from(byTool.entries())
        .map(([tool, v]) => ({ tool, executions: v.count, minutes_saved: v.minutes }))
        .sort((a, b) => b.minutes_saved - a.minutes_saved)
        .slice(0, 5);
    log.debug({ tenantId, totalActions, minutesAll }, 'ROI computed');
    return {
        hours_saved_total: Math.round((minutesAll / 60) * 10) / 10,
        hours_saved_this_month: Math.round((minutesMonth / 60) * 10) / 10,
        hours_saved_today: Math.round((minutesToday / 60) * 10) / 10,
        revenue_attributed_total: revenueAll,
        revenue_attributed_this_month: revenueMonth,
        top_contributors: topContributors,
        current_streak_days: currentStreak,
        longest_streak_days: longestStreak,
        total_actions: totalActions,
    };
}
//# sourceMappingURL=roi.js.map