import { authMiddleware } from '../auth/middleware.js';
import { supabase } from '../utils/supabase.js';
import { computeRoi } from './roi.js';
import { evaluateAchievements, recentMoments } from './achievements.js';
import { createChildLogger } from '../utils/logger.js';
const log = createChildLogger('insights:routes');
async function resolveTenantId(req) {
    const uid = req.user?.id;
    if (!uid)
        return null;
    const { data } = await supabase.from('tenants').select('id').eq('owner_id', uid).maybeSingle();
    return data?.id ?? null;
}
export async function registerInsightsRoutes(app) {
    // ─── ROI snapshot — the killer headline number ──────────────────────
    app.get('/api/insights/roi', { preHandler: [authMiddleware] }, async (req, reply) => {
        const tid = await resolveTenantId(req);
        if (!tid)
            return reply.status(401).send({ error: 'no tenant' });
        try {
            const snapshot = await computeRoi(tid);
            reply.send({ success: true, data: snapshot });
        }
        catch (err) {
            log.error({ error: err.message }, 'computeRoi failed');
            reply.status(500).send({ success: false, error: 'Failed to compute ROI' });
        }
    });
    // ─── Real ZATCA-tied sales counter ─────────────────────────────────
    // Counts successful zatca_invoice tool calls in the period + sums the
    // SAR amounts from the journal entry metadata. NO fake counter — a sale
    // only counts when the agent actually issued a real ZATCA invoice via
    // the tool (which means CR + tax number were verified at issue time).
    app.get('/api/insights/sales', { preHandler: [authMiddleware] }, async (req, reply) => {
        const tid = await resolveTenantId(req);
        if (!tid)
            return reply.status(401).send({ error: 'no tenant' });
        const range = req.query?.range ?? 'month';
        const days = range === 'today' ? 1 : range === 'week' ? 7 : range === 'year' ? 365 : 30;
        const cutoff = new Date(Date.now() - days * 86400_000).toISOString();
        try {
            const { data: rows } = await supabase
                .from('journal_entries')
                .select('action, outcome, metadata, created_at')
                .eq('tenant_id', tid)
                .eq('kind', 'tool')
                .gte('created_at', cutoff)
                .limit(2000);
            let count = 0;
            let totalSar = 0;
            // Group by day for the bar chart
            const byDay = {};
            for (const r of (rows ?? [])) {
                // Only successful zatca_invoice calls count as sales
                const isZatca = r.action === 'zatca_invoice' || (r.metadata && r.metadata.tool === 'zatca_invoice');
                const succeeded = r.outcome === 'success' || r.outcome === 'ok';
                if (!isZatca || !succeeded)
                    continue;
                // Parse SAR amount from metadata. Look in multiple likely places:
                //   metadata.total, metadata.amount_sar, metadata.amount, output.total
                let amount = 0;
                const md = r.metadata || {};
                if (typeof md.total === 'number')
                    amount = md.total;
                else if (typeof md.amount_sar === 'number')
                    amount = md.amount_sar;
                else if (typeof md.amount === 'number')
                    amount = md.amount;
                else if (md.output && typeof md.output.total === 'number')
                    amount = md.output.total;
                count++;
                totalSar += amount;
                const day = String(r.created_at).slice(0, 10);
                if (!byDay[day])
                    byDay[day] = { count: 0, sar: 0 };
                byDay[day].count++;
                byDay[day].sar += amount;
            }
            reply.send({
                success: true,
                data: {
                    range,
                    invoices_issued: count,
                    total_sar: Number(totalSar.toFixed(2)),
                    by_day: byDay,
                    note: 'Only successful zatca_invoice tool calls counted. No projections, no fake values.',
                },
            });
        }
        catch (err) {
            log.error({ error: err.message }, 'sales query failed');
            reply.status(500).send({ success: false, error: 'sales unavailable' });
        }
    });
    // ─── Live activity feed (last 48h) ──────────────────────────────────
    // Returns Muassis's recent autonomous actions in reverse-chrono order.
    // Each entry is { id, kind, title, summary, timestamp, status, icon }.
    // Used by the dashboard's right-rail "What Muassis did while you slept"
    // panel — creates the gut feeling that the agent is always on.
    app.get('/api/insights/feed', { preHandler: [authMiddleware] }, async (req, reply) => {
        const tid = await resolveTenantId(req);
        if (!tid)
            return reply.status(401).send({ error: 'no tenant' });
        const cutoff = new Date(Date.now() - 48 * 3600 * 1000).toISOString();
        try {
            const { data: rows } = await supabase
                .from('journal_entries')
                .select('id, kind, action, outcome, reason, metadata, created_at')
                .eq('tenant_id', tid)
                .gte('created_at', cutoff)
                .order('created_at', { ascending: false })
                .limit(40);
            const entries = (rows ?? []).map((r) => ({
                id: r.id,
                kind: r.kind,
                action: r.action,
                title: humanTitle(r.kind, r.action),
                summary: r.reason || (r.metadata && (r.metadata.summary || r.metadata.preview)) || '',
                status: r.outcome === 'failed' || r.outcome === 'error' ? 'error' : 'ok',
                icon: iconFor(r.kind, r.action),
                timestamp: r.created_at,
            }));
            reply.send({ success: true, data: entries });
        }
        catch (err) {
            log.error({ error: err.message }, 'feed query failed');
            reply.status(500).send({ success: false, error: 'feed unavailable' });
        }
    });
    // ─── Smart suggestions — "what should I work on right now?" ─────────
    // Surfaces top-3 highest-leverage actions for today. Heuristics:
    //   1. Stale-but-warm customers (active in last 14d, no contact today)
    //   2. Active agenda goals due soon
    //   3. Pending escalations from the inbound-allowlist flow
    //   4. Cron output that flagged something (compliance, vendor pricing)
    // Each suggestion is actionable: clicking dispatches the right tool.
    app.get('/api/insights/suggestions', { preHandler: [authMiddleware] }, async (req, reply) => {
        const tid = await resolveTenantId(req);
        if (!tid)
            return reply.status(401).send({ error: 'no tenant' });
        try {
            const suggestions = [];
            // 1) Pending escalations — fastest payback
            const { data: escs } = await supabase
                .from('escalation_queue').select('id, title, context, risk_level, created_at')
                .eq('tenant_id', tid).eq('status', 'pending').order('created_at', { ascending: false }).limit(3);
            for (const e of (escs ?? [])) {
                suggestions.push({
                    id: `esc:${e.id}`,
                    icon: '🟠',
                    title: e.title,
                    detail: (e.context || '').slice(0, 120),
                    action: { tool: 'open_escalation', args: { id: e.id } },
                    priority: e.risk_level === 'high' ? 100 : 80,
                });
            }
            // 2) Active goals close to due
            const { data: goals } = await supabase
                .from('goals').select('id, title, due_at, progress, target')
                .eq('tenant_id', tid).eq('archived', false).limit(10);
            const soon = (goals ?? [])
                .filter((g) => g.due_at)
                .map((g) => ({ ...g, dueIn: new Date(g.due_at).getTime() - Date.now() }))
                .filter((g) => g.dueIn > 0 && g.dueIn < 3 * 24 * 3600 * 1000)
                .sort((a, b) => a.dueIn - b.dueIn)
                .slice(0, 2);
            for (const g of soon) {
                const hours = Math.round(g.dueIn / 3600000);
                suggestions.push({
                    id: `goal:${g.id}`,
                    icon: '🎯',
                    title: g.title,
                    detail: `يستحق خلال ${hours} ساعة (${g.progress ?? 0} / ${g.target ?? '?'})`,
                    action: { tool: 'progress_goal', args: { id: g.id } },
                    priority: 70,
                });
            }
            // 3) Customers active in last 14d but not contacted today — re-engage
            const day14 = new Date(Date.now() - 14 * 86400000).toISOString();
            const today = new Date();
            today.setHours(0, 0, 0, 0);
            const { data: cust } = await supabase
                .from('customers').select('id, name, phone, last_contact')
                .eq('tenant_id', tid).gte('last_contact', day14).lt('last_contact', today.toISOString()).limit(5);
            const stale = (cust ?? []).slice(0, 2);
            for (const c of stale) {
                suggestions.push({
                    id: `cust:${c.id}`,
                    icon: '👤',
                    title: `تابع ${c.name || c.phone}`,
                    detail: `آخر تواصل ${new Date(c.last_contact).toLocaleDateString('ar-SA', { day: 'numeric', month: 'short' })} — حافظ على الزخم`,
                    action: { tool: 'followup_customer', args: { customer_id: c.id } },
                    priority: 50,
                });
            }
            // 4) If nothing else, encourage exploration
            if (suggestions.length === 0) {
                suggestions.push({
                    id: 'explore',
                    icon: '✨',
                    title: 'استكشف ما يقدر يسويه المؤسس',
                    detail: 'جرّب: "ابحث آخر أخبار AI" أو "أنشئ فاتورة ZATCA تجريبية" أو "جدول تذكير في ٥ دقائق"',
                    action: { tool: 'chat', args: { message: 'ما هي قدراتك؟' } },
                    priority: 1,
                });
            }
            suggestions.sort((a, b) => b.priority - a.priority);
            reply.send({ success: true, data: suggestions.slice(0, 3) });
        }
        catch (err) {
            log.error({ error: err.message }, 'suggestions failed');
            reply.status(500).send({ success: false, error: 'suggestions unavailable' });
        }
    });
    // ─── Today's morning brief ──────────────────────────────────────────
    // Pulls the latest cron-generated morning-brief message + today's
    // numbers + top suggestions into one card payload. Dashboard shows
    // this above the chat on first load each day.
    app.get('/api/insights/morning-brief', { preHandler: [authMiddleware] }, async (req, reply) => {
        const tid = await resolveTenantId(req);
        if (!tid)
            return reply.status(401).send({ error: 'no tenant' });
        try {
            const todayStart = new Date();
            todayStart.setHours(0, 0, 0, 0);
            // Latest cron message tagged morning-brief
            const { data: msgs } = await supabase
                .from('messages').select('id, content, created_at')
                .eq('tenant_id', tid).eq('direction', 'outbound')
                .like('content', '%morning-brief%')
                .gte('created_at', todayStart.toISOString())
                .order('created_at', { ascending: false }).limit(1);
            const brief = (msgs && msgs[0]) || null;
            const roi = await computeRoi(tid);
            // Today's outbound count
            const { count: outboundToday } = await supabase
                .from('messages').select('*', { count: 'exact', head: true })
                .eq('tenant_id', tid).eq('direction', 'outbound')
                .gte('created_at', todayStart.toISOString());
            reply.send({
                success: true,
                data: {
                    generated_at: brief ? brief.created_at : null,
                    content: brief ? brief.content : null,
                    stats_today: {
                        hours_saved: roi.hours_saved_today,
                        messages_sent: outboundToday ?? 0,
                        streak: roi.current_streak_days,
                    },
                    has_brief: !!brief,
                },
            });
        }
        catch (err) {
            log.error({ error: err.message }, 'morning-brief failed');
            reply.status(500).send({ success: false, error: 'brief unavailable' });
        }
    });
    // ─── Achievements ───────────────────────────────────────────────────
    app.get('/api/insights/achievements', { preHandler: [authMiddleware] }, async (req, reply) => {
        const tid = await resolveTenantId(req);
        if (!tid)
            return reply.status(401).send({ error: 'no tenant' });
        try {
            const achievements = await evaluateAchievements(tid);
            const unlocked = achievements.filter(a => a.unlocked).length;
            reply.send({
                success: true,
                data: {
                    achievements,
                    unlocked_count: unlocked,
                    total_count: achievements.length,
                    completion_pct: Math.round((unlocked / achievements.length) * 100),
                },
            });
        }
        catch (err) {
            log.error({ error: err.message }, 'achievements failed');
            reply.status(500).send({ success: false, error: 'achievements unavailable' });
        }
    });
    // ─── Muassis Moments — recent celebration-worthy events ─────────────
    app.get('/api/insights/moments', { preHandler: [authMiddleware] }, async (req, reply) => {
        const tid = await resolveTenantId(req);
        if (!tid)
            return reply.status(401).send({ error: 'no tenant' });
        try {
            const moments = await recentMoments(tid);
            reply.send({ success: true, data: moments });
        }
        catch (err) {
            reply.status(500).send({ success: false, error: 'moments unavailable' });
        }
    });
    // ─── Overview metrics — the right-panel "نظرة عامة" widgets ──────────
    // Returns the three real numbers the dashboard sidebar shows. Every value
    // is computed from the live DB — no placeholders, no fabricated deltas.
    //   active_conversations — distinct non-system customers messaged in 7d
    //   tasks_in_progress    — active agenda goals + pending approvals
    //   transactions_today   — ZATCA invoices issued since Riyadh midnight
    app.get('/api/insights/overview', { preHandler: [authMiddleware] }, async (req, reply) => {
        const tid = await resolveTenantId(req);
        if (!tid)
            return reply.status(401).send({ error: 'no tenant' });
        try {
            // active_conversations: distinct customers (excluding the system-cron
            // pseudo-customer) with a message in the last 7 days.
            const sevenDaysAgo = new Date(Date.now() - 7 * 86400_000).toISOString();
            const { data: cronRow } = await supabase
                .from('customers').select('id').eq('tenant_id', tid).eq('phone', 'system-cron').maybeSingle();
            const cronId = cronRow?.id;
            const { data: recentMsgs } = await supabase
                .from('messages').select('customer_id, created_at').eq('tenant_id', tid).gt('created_at', sevenDaysAgo).limit(5000);
            const activeSet = new Set();
            for (const m of recentMsgs ?? []) {
                if (!m.customer_id || m.customer_id === cronId)
                    continue;
                activeSet.add(m.customer_id);
            }
            // tasks_in_progress: active goals + pending approvals (work the owner
            // has on the table right now).
            const { data: goals } = await supabase
                .from('agenda_goals').select('id, status').eq('tenant_id', tid).eq('status', 'active').limit(1000);
            const { data: approvals } = await supabase
                .from('approval_requests').select('id, status').eq('tenant_id', tid).eq('status', 'pending').limit(1000);
            const tasksInProgress = (goals?.length ?? 0) + (approvals?.length ?? 0);
            // transactions_today: ZATCA invoices created since 00:00 Riyadh (UTC+3).
            const now = new Date();
            const riyadhMs = now.getTime() + 3 * 3600_000;
            const riyadhMidnightUtc = new Date(Math.floor(riyadhMs / 86400_000) * 86400_000 - 3 * 3600_000).toISOString();
            const { data: invoices } = await supabase
                .from('zatca_invoices').select('id, created_at').eq('tenant_id', tid).gt('created_at', riyadhMidnightUtc).limit(1000);
            reply.send({
                success: true,
                data: {
                    active_conversations: activeSet.size,
                    tasks_in_progress: tasksInProgress,
                    transactions_today: invoices?.length ?? 0,
                },
            });
        }
        catch (err) {
            log.error({ error: err.message }, 'overview metrics failed');
            reply.status(500).send({ success: false, error: 'overview unavailable' });
        }
    });
    log.info('Insights routes registered ✅');
}
function humanTitle(kind, action) {
    if (kind === 'tool' && action) {
        const map = {
            send_email: 'أرسل بريداً', send_whatsapp: 'أرسل WhatsApp', send_telegram: 'أرسل Telegram',
            web_search: 'بحث في الإنترنت', web_fetch: 'جلب صفحة', research_topic: 'بحث معمّق',
            create_pdf_report: 'أنشأ تقرير PDF', create_excel: 'أنشأ ملف Excel', html_to_pdf: 'حوّل لـ PDF',
            write_file: 'حفظ ملف', read_file: 'قراءة ملف', learn_from_document: 'تعلّم من مستند',
            zatca_invoice: 'فاتورة ZATCA', send_quote: 'أرسل عرض سعر', issue_invoice: 'أصدر فاتورة',
            schedule_meeting: 'جدول اجتماع', schedule_once: 'جدول تذكير', schedule_recurring: 'جدول متكرر',
            log_lead: 'سجّل عميل محتمل', qualify_lead: 'أهّل عميل', remember_fact: 'حفظ معلومة',
            browser_navigate: 'تصفّح موقع', browser_take_screenshot: 'لقطة شاشة',
        };
        return map[action] || action.replace(/_/g, ' ');
    }
    if (kind === 'cron')
        return 'مهمة مجدولة';
    if (kind === 'escalation')
        return 'يحتاج موافقتك';
    if (kind === 'kg_extraction')
        return 'استخرج معرفة';
    return kind;
}
function iconFor(kind, action) {
    if (action?.startsWith('send_'))
        return '✉️';
    if (action?.startsWith('browser_'))
        return '🌐';
    if (action?.includes('search') || action?.includes('fetch') || action?.includes('research'))
        return '🔍';
    if (action?.includes('pdf') || action?.includes('excel') || action?.includes('document') || action?.includes('file'))
        return '📄';
    if (action?.includes('invoice') || action?.includes('quote') || action?.includes('zatca'))
        return '💰';
    if (action?.includes('schedule') || action?.includes('meeting'))
        return '⏰';
    if (action?.includes('lead') || action?.includes('customer'))
        return '👤';
    if (kind === 'cron')
        return '🕒';
    if (kind === 'escalation')
        return '🟠';
    return '⚙️';
}
//# sourceMappingURL=routes.js.map