/**
 * src/tenants/routes.ts
 * مسارات API إدارة المستأجرين — Fastify routes
 */
import { z } from 'zod';
import { createChildLogger } from '../utils/logger.js';
import { createTenant, createTenantSchema, getTenantById, updateTenant, listCustomers, } from './manager.js';
import { supabase } from '../utils/supabase.js';
import { authMiddleware, tenantMiddleware } from '../auth/middleware.js';
import { getOrCreateSession, peekSession, disconnectSession, resetSession, sendBaileysMessageToJid, requestPairingCode } from '../channels/whatsapp/baileys-adapter.js';
const log = createChildLogger('tenants:routes');
// ─── Route Registration ──────────────────────────────────────────────
export async function registerTenantRoutes(app) {
    // ─── POST /api/tenants — إنشاء مستأجر جديد ───
    // owner_id is always derived from the authenticated user — clients can't spoof it.
    const createTenantBodySchema = createTenantSchema.omit({ owner_id: true });
    app.post('/api/tenants', { preHandler: [authMiddleware] }, async (request, reply) => {
        try {
            const body = createTenantBodySchema.parse(request.body);
            const user = request.user;
            const tenant = await createTenant({ ...body, owner_id: user.id });
            log.info({ tenantId: tenant.id, business: tenant.business_name, ownerId: user.id }, 'Tenant created via API');
            return reply.status(201).send({
                success: true,
                data: tenant,
            });
        }
        catch (err) {
            if (err instanceof z.ZodError) {
                return reply.status(400).send({
                    success: false,
                    error: 'Validation error',
                    details: err.issues.map(i => ({
                        field: i.path.join('.'),
                        message: i.message,
                    })),
                });
            }
            const message = err instanceof Error ? err.message : 'Unknown error';
            log.error({ error: message }, 'Failed to create tenant');
            // رقم مكرر
            if (message.includes('already registered')) {
                return reply.status(409).send({ success: false, error: message });
            }
            return reply.status(500).send({ success: false, error: 'Internal server error' });
        }
    });
    // ─── GET /api/tenants/:id — معلومات المستأجر ───
    app.get('/api/tenants/:id', { preHandler: [authMiddleware, tenantMiddleware] }, async (request, reply) => {
        try {
            const { id } = request.params;
            const tenant = await getTenantById(id);
            if (!tenant) {
                return reply.status(404).send({ success: false, error: 'Tenant not found' });
            }
            return reply.send({ success: true, data: tenant });
        }
        catch (err) {
            log.error({ error: err instanceof Error ? err.message : String(err) }, 'Failed to get tenant');
            return reply.status(500).send({ success: false, error: 'Internal server error' });
        }
    });
    // ─── PUT /api/tenants/:id — تحديث المستأجر ───
    const updateSchema = z.object({
        business_name: z.string().min(1).max(200).optional(),
        business_name_ar: z.string().optional(),
        agent_name: z.string().min(1).max(100).optional(),
        owner_name: z.string().min(1).max(200).optional(),
        owner_email: z.string().email().optional(),
        plan: z.enum(['trial', 'starter', 'pro', 'enterprise']).optional(),
        status: z.enum(['active', 'suspended', 'trial']).optional(),
        settings: z.record(z.string(), z.unknown()).optional(),
    });
    app.put('/api/tenants/:id', { preHandler: [authMiddleware, tenantMiddleware] }, async (request, reply) => {
        try {
            const { id } = request.params;
            // التحقق من وجود المستأجر
            const existing = await getTenantById(id);
            if (!existing) {
                return reply.status(404).send({ success: false, error: 'Tenant not found' });
            }
            const updates = updateSchema.parse(request.body);
            const tenant = await updateTenant(id, updates);
            return reply.send({ success: true, data: tenant });
        }
        catch (err) {
            if (err instanceof z.ZodError) {
                return reply.status(400).send({
                    success: false,
                    error: 'Validation error',
                    details: err.issues.map(i => ({
                        field: i.path.join('.'),
                        message: i.message,
                    })),
                });
            }
            log.error({ error: err instanceof Error ? err.message : String(err) }, 'Failed to update tenant');
            return reply.status(500).send({ success: false, error: 'Internal server error' });
        }
    });
    // ─── GET /api/tenants/:id/customers — قائمة العملاء ───
    app.get('/api/tenants/:id/customers', { preHandler: [authMiddleware, tenantMiddleware] }, async (request, reply) => {
        try {
            const { id } = request.params;
            const limit = Math.min(parseInt(request.query.limit ?? '50', 10) || 50, 200);
            const offset = parseInt(request.query.offset ?? '0', 10) || 0;
            // التحقق من وجود المستأجر
            const tenant = await getTenantById(id);
            if (!tenant) {
                return reply.status(404).send({ success: false, error: 'Tenant not found' });
            }
            const result = await listCustomers(id, { limit, offset });
            return reply.send({
                success: true,
                data: result.customers,
                pagination: {
                    total: result.total,
                    limit,
                    offset,
                    hasMore: offset + limit < result.total,
                },
            });
        }
        catch (err) {
            log.error({ error: err instanceof Error ? err.message : String(err) }, 'Failed to list customers');
            return reply.status(500).send({ success: false, error: 'Internal server error' });
        }
    });
    // ─── GET /api/tenants/:id/conversations — list customers with last-message snapshot ───
    // Powers the dashboard's live conversations view. Joins each customer with
    // their most-recent message via a single round-trip. Tail-pagination by
    // last_contact desc so the freshest threads sit on top.
    app.get('/api/tenants/:id/conversations', { preHandler: [authMiddleware, tenantMiddleware] }, async (request, reply) => {
        try {
            const { id: tenantId } = request.params;
            const limit = Math.min(parseInt(request.query.limit ?? '50', 10) || 50, 200);
            const statusFilter = request.query.status;
            // Pull customers ordered by last_contact desc.
            let custQuery = supabase
                .from('customers')
                .select('id, name, phone, dialect, last_contact, total_messages, status')
                .eq('tenant_id', tenantId)
                .order('last_contact', { ascending: false })
                .limit(limit);
            if (statusFilter)
                custQuery = custQuery.eq('status', statusFilter);
            const { data: customers, error: custErr } = await custQuery;
            if (custErr) {
                log.error({ tenantId, error: custErr.message }, 'Failed to load customers for conversations');
                return reply.status(500).send({ success: false, error: 'Database error' });
            }
            if (!customers || customers.length === 0) {
                return reply.send({ success: true, data: [] });
            }
            const customerIds = customers.map(c => c.id);
            // Pull the latest inbound|outbound message per customer in one pass.
            // We order desc + limit per customer via RPC, but for simplicity grab
            // the recent N messages and pick latest in JS.
            const { data: messages, error: msgErr } = await supabase
                .from('messages')
                .select('customer_id, content, direction, created_at, type')
                .eq('tenant_id', tenantId)
                .in('customer_id', customerIds)
                .order('created_at', { ascending: false })
                .limit(limit * 4); // ~4 messages per customer in window
            if (msgErr) {
                log.warn({ tenantId, error: msgErr.message }, 'Failed to load latest messages — returning customers without snippets');
            }
            const latestByCustomer = new Map();
            for (const m of (messages || [])) {
                if (!latestByCustomer.has(m.customer_id)) {
                    latestByCustomer.set(m.customer_id, m);
                }
            }
            const data = customers.map(c => {
                const latest = latestByCustomer.get(c.id);
                return {
                    id: c.id,
                    name: c.name || c.phone,
                    phone: c.phone,
                    dialect: c.dialect,
                    last_contact: c.last_contact,
                    total_messages: c.total_messages,
                    status: c.status,
                    last_message: latest?.content || null,
                    last_message_direction: latest?.direction || null,
                    last_message_at: latest?.created_at || c.last_contact,
                    last_message_type: latest?.type || null,
                };
            });
            return reply.send({ success: true, data });
        }
        catch (err) {
            log.error({ error: err instanceof Error ? err.message : String(err) }, 'conversations endpoint failed');
            return reply.status(500).send({ success: false, error: 'Internal server error' });
        }
    });
    // ─── GET /api/auth/my-tenant — primary tenant of the logged-in user ───
    // Resolves the user's tenant via tenants.owner_id (canonical link to auth.users.id).
    // Lets the dashboard skip onboarding-redirect logic for returning users.
    app.get('/api/auth/my-tenant', { preHandler: [authMiddleware] }, async (request, reply) => {
        try {
            const user = request.user;
            // Sort by created_at DESC + take 1 — returns the LATEST tenant for
            // this owner. Without this, when an owner has multiple tenants (e.g.
            // they ran `muassis init` more than once with different agent names),
            // SQLite returned arbitrary order and the dashboard would show stale
            // identity (old "Mohammed" instead of newly-created "Ahmed"). Same
            // pattern as resolveDeviceTenant() in src/autonomy/boot.ts.
            const { data: rows, error } = await supabase
                .from('tenants')
                .select('id, business_name, business_name_ar, agent_name, whatsapp_phone, plan, status, language, created_at')
                .eq('owner_id', user.id)
                .in('status', ['active', 'trial'])
                .order('created_at', { ascending: false })
                .limit(1);
            const data = rows?.[0] ?? null;
            if (error) {
                log.warn({ userId: user.id, error: error.message }, 'my-tenant lookup failed');
                return reply.send({ success: true, data: null });
            }
            return reply.send({ success: true, data });
        }
        catch (err) {
            log.error({ error: err instanceof Error ? err.message : String(err) }, 'my-tenant failed');
            return reply.status(500).send({ success: false, error: 'Internal server error' });
        }
    });
    // ─── GET /api/tenants/:id/analytics — live overview metrics ───
    // Powers the dashboard's "نظرة عامة" card. Returns three counters that
    // reflect REAL activity:
    //   1) active_today        — distinct customers (incl. the local "Web user")
    //                            with messages today, plus +1 if there were any
    //                            web-chat messages today (so the owner's own
    //                            chat counts as a "live conversation")
    //   2) pending_tasks       — active agenda goals (not archived/completed)
    //   3) transactions_today  — tool actions logged in the journal today
    //                            (kind='tool' OR cron executions today)
    // Also returns deltas vs. yesterday so the dashboard can render
    // "+12% من الأمس" style indicators truthfully.
    app.get('/api/tenants/:id/analytics', { preHandler: [authMiddleware, tenantMiddleware] }, async (request, reply) => {
        try {
            const { id } = request.params;
            const tenant = await getTenantById(id);
            if (!tenant) {
                return reply.status(404).send({ success: false, error: 'Tenant not found' });
            }
            const now = new Date();
            const todayStr = now.toISOString().split('T')[0];
            const yesterday = new Date(now.getTime() - 86_400_000);
            const yesterdayStr = yesterday.toISOString().split('T')[0];
            // ── Conversations: customers who exchanged a message today ──
            const [{ count: totalCustomers }, { count: todayCustomers }, { count: yesterdayCustomers }] = await Promise.all([
                supabase.from('customers').select('*', { count: 'exact', head: true }).eq('tenant_id', id),
                supabase.from('customers').select('*', { count: 'exact', head: true }).eq('tenant_id', id).gte('last_seen_at', `${todayStr}T00:00:00`),
                supabase.from('customers').select('*', { count: 'exact', head: true }).eq('tenant_id', id).gte('last_seen_at', `${yesterdayStr}T00:00:00`).lt('last_seen_at', `${todayStr}T00:00:00`),
            ]);
            // Total cumulative messages (best-effort sum of per-customer counters)
            const { data: messageStats } = await supabase
                .from('customers')
                .select('message_count')
                .eq('tenant_id', id);
            const totalMessages = messageStats?.reduce((sum, c) => sum + (c.message_count ?? 0), 0) ?? 0;
            // Messages today via the messages table — captures owner's own chat
            // activity (which creates a "Web user" customer) so the overview
            // never reads as "0 conversations" when there's clearly active chat.
            const { count: messagesTodayCount } = await supabase
                .from('messages')
                .select('*', { count: 'exact', head: true })
                .eq('tenant_id', id)
                .gte('created_at', `${todayStr}T00:00:00`);
            const { count: messagesYesterdayCount } = await supabase
                .from('messages')
                .select('*', { count: 'exact', head: true })
                .eq('tenant_id', id)
                .gte('created_at', `${yesterdayStr}T00:00:00`)
                .lt('created_at', `${todayStr}T00:00:00`);
            // ── Tasks: count of active agenda goals ──
            const { count: activeGoals } = await supabase
                .from('goals')
                .select('*', { count: 'exact', head: true })
                .eq('tenant_id', id)
                .eq('archived', false);
            // ── Transactions today: journal entries of kind 'tool' + crons run today ──
            // cron_executions has executed_at column; wrap in Promise.resolve so a
            // missing-table error doesn't break the analytics endpoint.
            const cronTodayPromise = (async () => {
                try {
                    const r = await supabase.from('cron_executions').select('*', { count: 'exact', head: true }).eq('tenant_id', id).gte('executed_at', `${todayStr}T00:00:00`);
                    return { count: r.count ?? 0 };
                }
                catch {
                    return { count: 0 };
                }
            })();
            const [{ count: toolJournalToday }, { count: toolJournalYesterday }, { count: cronToday }] = await Promise.all([
                supabase.from('journal_entries').select('*', { count: 'exact', head: true }).eq('tenant_id', id).eq('kind', 'tool').gte('created_at', `${todayStr}T00:00:00`),
                supabase.from('journal_entries').select('*', { count: 'exact', head: true }).eq('tenant_id', id).eq('kind', 'tool').gte('created_at', `${yesterdayStr}T00:00:00`).lt('created_at', `${todayStr}T00:00:00`),
                cronTodayPromise,
            ]);
            const transactionsToday = (toolJournalToday ?? 0) + (cronToday ?? 0);
            const transactionsYesterday = (toolJournalYesterday ?? 0);
            // ── Pct deltas vs. yesterday (or "—" when yesterday is zero) ──
            const pct = (today, yesterday) => {
                if (yesterday === 0)
                    return { value: 0, label: today > 0 ? 'جديد اليوم' : null };
                const delta = Math.round(((today - yesterday) / yesterday) * 100);
                const sign = delta > 0 ? '+' : '';
                return { value: delta, label: `${sign}${delta}% من الأمس` };
            };
            // Take the larger of (distinct-customers-today) and (1 if any messages today)
            // so the owner's own chat doesn't sit at zero. The number reflects "live
            // conversations" the agent is engaged in.
            const activeConversations = Math.max(todayCustomers ?? 0, (messagesTodayCount ?? 0) > 0 ? 1 : 0);
            const activeConversationsYesterday = Math.max(yesterdayCustomers ?? 0, (messagesYesterdayCount ?? 0) > 0 ? 1 : 0);
            return reply.send({
                success: true,
                data: {
                    tenant_id: id,
                    // Live metrics
                    active_today: activeConversations,
                    active_today_delta: pct(activeConversations, activeConversationsYesterday),
                    pending_tasks: activeGoals ?? 0,
                    transactions_today: transactionsToday,
                    transactions_today_delta: pct(transactionsToday, transactionsYesterday),
                    // Supporting metrics
                    total_customers: totalCustomers ?? 0,
                    total_messages: totalMessages,
                    messages_today: messagesTodayCount ?? 0,
                    plan: tenant.plan,
                    status: tenant.status,
                },
            });
        }
        catch (err) {
            log.error({ error: err instanceof Error ? err.message : String(err) }, 'Failed to get analytics');
            return reply.status(500).send({ success: false, error: 'Internal server error' });
        }
    });
    // ─── GET /api/tenants/:id/whatsapp/status — read-only Baileys session peek ───
    // The dashboard polls this every 1.5s while QR is pending. MUST be a pure
    // read: previously this called getOrCreateSession, which silently burned
    // pair-attempt budget on every poll and locked users out of pairing for
    // 10–20 min ("WhatsApp won't connect even via QR" bug). Now uses
    // peekSession which never opens a new Baileys socket.
    app.get('/api/tenants/:id/whatsapp/status', { preHandler: [authMiddleware, tenantMiddleware] }, async (request, reply) => {
        try {
            const status = await peekSession(request.params.id);
            return reply.send({ success: true, data: status });
        }
        catch (err) {
            log.error({ error: err instanceof Error ? err.message : String(err) }, 'whatsapp status endpoint failed');
            return reply.code(500).send({ success: false, error: 'Failed to read WhatsApp session status' });
        }
    });
    // ─── POST /api/tenants/:id/whatsapp/qr — start Baileys pairing ───
    app.post('/api/tenants/:id/whatsapp/qr', { preHandler: [authMiddleware, tenantMiddleware] }, async (request, reply) => {
        try {
            const status = await getOrCreateSession(request.params.id);
            return reply.send({ success: true, data: status });
        }
        catch (err) {
            log.error({ error: err instanceof Error ? err.message : String(err) }, 'whatsapp QR endpoint failed');
            return reply.code(500).send({ success: false, error: 'Failed to start WhatsApp session' });
        }
    });
    // ─── POST /api/tenants/:id/whatsapp/pair-code — alternate pairing ───
    // Request an 8-character pairing code for the given phone number, then
    // the owner types it in WhatsApp → Settings → Linked Devices → Link
    // with phone number. Avoids the QR-rotation-mid-scan failure mode.
    app.post('/api/tenants/:id/whatsapp/pair-code', { preHandler: [authMiddleware, tenantMiddleware] }, async (request, reply) => {
        try {
            const phone = (request.body?.phone ?? '').toString();
            if (!phone || phone.replace(/[^\d]/g, '').length < 10) {
                return reply.status(400).send({ success: false, error: 'phone required (E.164 like +966581978135)' });
            }
            const code = await requestPairingCode(request.params.id, phone);
            // Format as XXXX-XXXX for easier typing
            const pretty = code.length === 8 ? `${code.slice(0, 4)}-${code.slice(4)}` : code;
            return reply.send({ success: true, data: { code, formatted: pretty, phone } });
        }
        catch (err) {
            log.error({ error: err instanceof Error ? err.message : String(err) }, 'pair-code request failed');
            return reply.code(500).send({ success: false, error: err instanceof Error ? err.message : String(err) });
        }
    });
    // ─── POST /api/tenants/:id/whatsapp/disconnect — log out ───
    app.post('/api/tenants/:id/whatsapp/disconnect', { preHandler: [authMiddleware, tenantMiddleware] }, async (request, reply) => {
        try {
            await disconnectSession(request.params.id);
            return reply.send({ success: true, data: { status: 'logged_out' } });
        }
        catch (err) {
            log.error({ error: err instanceof Error ? err.message : String(err) }, 'whatsapp disconnect failed');
            return reply.code(500).send({ success: false, error: 'Failed to disconnect' });
        }
    });
    // ─── POST /api/tenants/:id/whatsapp/reset — wipe creds + fresh pair ───
    // Used when Baileys creds land half-paired (interrupted scan, device
    // removed from WhatsApp's linked-devices list later, etc.). Returns the
    // fresh session status — typically status=pending_qr with a brand-new
    // QR in qr_png that the dashboard can render immediately.
    app.post('/api/tenants/:id/whatsapp/reset', { preHandler: [authMiddleware, tenantMiddleware] }, async (request, reply) => {
        try {
            await resetSession(request.params.id);
            const fresh = await getOrCreateSession(request.params.id);
            return reply.send({ success: true, data: fresh });
        }
        catch (err) {
            log.error({ error: err instanceof Error ? err.message : String(err) }, 'whatsapp reset failed');
            return reply.code(500).send({ success: false, error: 'Failed to reset WhatsApp session' });
        }
    });
    // ─── WhatsApp pending-approval flow ──────────────────────────────────
    // When a new sender DMs the agent and policy=ask, an escalation gets
    // queued with a JSON payload embedded in `context`. These endpoints let
    // the dashboard list those pending senders + approve or deny each one.
    // GET /api/whatsapp/approvals/pending — list pending sender approvals
    app.get('/api/whatsapp/approvals/pending', {
        preHandler: [authMiddleware],
    }, async (request, reply) => {
        try {
            const user = request.user;
            const { data: tenants } = await supabase
                .from('tenants').select('id').eq('owner_id', user.id).limit(1);
            const tenantId = tenants?.[0]?.id;
            if (!tenantId)
                return reply.status(404).send({ success: false, error: 'no tenant' });
            const { data: rows } = await supabase
                .from('escalation_queue')
                .select('*')
                .eq('tenant_id', tenantId)
                .eq('status', 'pending')
                .order('created_at', { ascending: false });
            const approvals = (rows ?? [])
                .map((r) => {
                const m = /__WA_APPROVAL_PAYLOAD__:(\{.*\})/s.exec(r.context);
                if (!m)
                    return null;
                try {
                    const payload = JSON.parse(m[1]);
                    return {
                        id: r.id,
                        title: r.title,
                        created_at: r.created_at,
                        phone: payload.phone,
                        profile_name: payload.profileName,
                        remote_jid: payload.remoteJid,
                        text: payload.text,
                        received_at: payload.receivedAt,
                    };
                }
                catch {
                    return null;
                }
            })
                .filter(Boolean);
            return reply.send({ success: true, data: approvals });
        }
        catch (err) {
            log.error({ error: err instanceof Error ? err.message : String(err) }, 'list WA approvals failed');
            return reply.status(500).send({ success: false, error: 'Internal server error' });
        }
    });
    // POST /api/whatsapp/approvals/:id/approve — allow the sender + welcome
    app.post('/api/whatsapp/approvals/:id/approve', { preHandler: [authMiddleware] }, async (request, reply) => {
        try {
            const user = request.user;
            const { data: tenants } = await supabase
                .from('tenants').select('id').eq('owner_id', user.id).limit(1);
            const tenantId = tenants?.[0]?.id;
            if (!tenantId)
                return reply.status(404).send({ success: false, error: 'no tenant' });
            const { id } = request.params;
            const { data: row } = await supabase
                .from('escalation_queue').select('*').eq('id', id).eq('tenant_id', tenantId).maybeSingle();
            if (!row)
                return reply.status(404).send({ success: false, error: 'approval not found' });
            const ctx = row.context ?? '';
            const m = /__WA_APPROVAL_PAYLOAD__:(\{.*\})/s.exec(ctx);
            if (!m)
                return reply.status(400).send({ success: false, error: 'no payload on this escalation' });
            const payload = JSON.parse(m[1]);
            // Add to allowed contacts so future messages bypass the gate
            const { addAllowed } = await import('../autonomy/allowlist.js');
            await addAllowed(tenantId, {
                channel: 'whatsapp',
                identifier: payload.phone,
                display_name: payload.profileName ?? undefined,
                added_by: 'owner',
                role: request.body?.add_as ?? 'customer',
                reason: 'approved via dashboard',
            });
            // Welcome the sender + process their original message through the
            // brain so they get a real response, not just "approved".
            try {
                const welcome = payload.profileName
                    ? `أهلاً ${payload.profileName} 👋\n\nتم قبول طلبك. كيف أقدر أساعدك؟`
                    : `أهلاً 👋\n\nتم قبول طلبك. كيف أقدر أساعدك؟`;
                await sendBaileysMessageToJid(tenantId, payload.remoteJid, welcome);
                // Hand the held message back to the brain so the sender gets a
                // substantive reply, not just the welcome line.
                const { processMessage } = await import('../agent/brain.js');
                // Find-or-create the customer for this approved sender so the
                // brain has a stable thread id to log messages against.
                let customerId;
                const existing = await supabase
                    .from('customers').select('id').eq('tenant_id', tenantId).eq('phone', payload.phone).maybeSingle();
                if (existing.data) {
                    customerId = existing.data.id;
                }
                else {
                    const { randomUUID } = await import('node:crypto');
                    customerId = randomUUID();
                    const now = new Date().toISOString();
                    await supabase.from('customers').insert({
                        id: customerId, tenant_id: tenantId, phone: payload.phone,
                        name: payload.profileName, language: 'ar', first_contact: now, last_contact: now,
                        total_messages: 0, status: 'active', metadata: { channel: 'whatsapp', approved_via: 'dashboard' },
                    });
                }
                const brainResp = await processMessage(tenantId, customerId, payload.text, { mediaType: 'text', channel: 'whatsapp', customerName: payload.profileName ?? payload.phone }, { enableTools: true, maxToolLoops: 4 });
                await sendBaileysMessageToJid(tenantId, payload.remoteJid, brainResp.response);
            }
            catch (err) {
                log.warn({ error: err.message }, 'approve: welcome/reply send failed');
            }
            // Mark the escalation resolved
            await supabase.from('escalation_queue').update({
                status: 'resolved', resolution: 'approved', resolved_at: new Date().toISOString(),
            }).eq('id', id);
            return reply.send({ success: true, data: { id, resolution: 'approved' } });
        }
        catch (err) {
            log.error({ error: err instanceof Error ? err.message : String(err) }, 'WA approve failed');
            return reply.status(500).send({ success: false, error: 'Internal server error' });
        }
    });
    // POST /api/whatsapp/approvals/:id/deny — polite decline + close
    app.post('/api/whatsapp/approvals/:id/deny', { preHandler: [authMiddleware] }, async (request, reply) => {
        try {
            const user = request.user;
            const { data: tenants } = await supabase
                .from('tenants').select('id').eq('owner_id', user.id).limit(1);
            const tenantId = tenants?.[0]?.id;
            if (!tenantId)
                return reply.status(404).send({ success: false, error: 'no tenant' });
            const { id } = request.params;
            const { data: row } = await supabase
                .from('escalation_queue').select('*').eq('id', id).eq('tenant_id', tenantId).maybeSingle();
            if (!row)
                return reply.status(404).send({ success: false, error: 'approval not found' });
            const ctx = row.context ?? '';
            const m = /__WA_APPROVAL_PAYLOAD__:(\{.*\})/s.exec(ctx);
            if (!m)
                return reply.status(400).send({ success: false, error: 'no payload' });
            const payload = JSON.parse(m[1]);
            try {
                const declineText = request.body?.reason
                    ? `شكراً لتواصلك. ${request.body.reason}`
                    : `شكراً لتواصلك 🙏 لكن صاحب الحساب ما يقدر يتابع طلبك حالياً. تحياتنا.`;
                await sendBaileysMessageToJid(tenantId, payload.remoteJid, declineText);
            }
            catch (err) {
                log.warn({ error: err.message }, 'deny: decline message send failed');
            }
            await supabase.from('escalation_queue').update({
                status: 'resolved', resolution: 'denied', resolved_at: new Date().toISOString(),
            }).eq('id', id);
            return reply.send({ success: true, data: { id, resolution: 'denied' } });
        }
        catch (err) {
            log.error({ error: err instanceof Error ? err.message : String(err) }, 'WA deny failed');
            return reply.status(500).send({ success: false, error: 'Internal server error' });
        }
    });
    log.info('Tenant routes registered ✅');
}
//# sourceMappingURL=routes.js.map