/*
|--------------------------------------------------------------------------
| Agência PSI — Edge Function: send-whatsapp-message
|--------------------------------------------------------------------------
| Envia mensagem WhatsApp via Evolution API da clínica. Registra a mensagem
| imediatamente em conversation_messages (direction=outbound) para feedback
| instantâneo no UI (via Realtime). Quando a Evolution ecoar a mensagem de
| volta pelo webhook com fromMe=true, o evolution-whatsapp-inbound faz UPDATE
| (não duplica) pelo provider_message_id.
|
| Body JSON:
|   {
|     "tenant_id":  "<uuid>",
|     "to_number":  "5516912345678",      // sem + nem @s.whatsapp.net
|     "body":       "Texto da mensagem",
|     "patient_id": "<uuid>" (opcional)   // se já vinculado
|   }
|--------------------------------------------------------------------------
*/

import { createClient } from 'https://esm.sh/@supabase/supabase-js@2'
import { adminClient, tenantDbForId } from '../_shared/tenant.ts'

const corsHeaders = {
    'Access-Control-Allow-Origin':  '*',
    'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
    'Access-Control-Allow-Methods': 'POST, OPTIONS',
}

function json(body: unknown, status = 200) {
    return new Response(JSON.stringify(body), {
        status,
        headers: { ...corsHeaders, 'Content-Type': 'application/json' }
    })
}

function sanitizeBody(raw: string): string {
    return String(raw ?? '')
        .replace(/[\x00-\x08\x0B\x0C\x0E-\x1F]/g, '')
        .trim()
        .slice(0, 4000)
}

function normalizePhone(raw: string): string {
    const digits = String(raw ?? '').replace(/\D/g, '')
    if (digits.length >= 12 && digits.startsWith('55')) return digits
    if (digits.length === 11 || digits.length === 10) return '55' + digits
    return digits
}

// ═══════════════════════════════════════════════════════════════════
// Twilio send helper (com dedução de crédito)
// ═══════════════════════════════════════════════════════════════════
type TwilioChannel = {
    twilio_subaccount_sid: string
    twilio_phone_number: string
    credentials: { subaccount_auth_token?: string }
}

async function sendViaTwilio(
    channel: TwilioChannel,
    toPhone: string,
    text: string
): Promise<{ ok: boolean; messageId?: string; status?: string; error?: string }> {
    const subSid = channel.twilio_subaccount_sid
    const authToken = channel.credentials?.subaccount_auth_token
    const fromNumber = channel.twilio_phone_number  // E.164: +5511999990000

    if (!subSid || !authToken || !fromNumber) {
        return { ok: false, error: 'Twilio credenciais incompletas' }
    }

    const endpoint = `https://api.twilio.com/2010-04-01/Accounts/${subSid}/Messages.json`
    const basicAuth = btoa(`${subSid}:${authToken}`)

    const toE164 = toPhone.startsWith('+') ? toPhone : `+${toPhone}`
    const params = new URLSearchParams()
    params.append('From', `whatsapp:${fromNumber}`)
    params.append('To', `whatsapp:${toE164}`)
    params.append('Body', text)

    try {
        const resp = await fetch(endpoint, {
            method: 'POST',
            headers: {
                'Authorization': `Basic ${basicAuth}`,
                'Content-Type': 'application/x-www-form-urlencoded'
            },
            body: params.toString()
        })
        const data = await resp.json().catch(() => null) as Record<string, unknown> | null
        if (!resp.ok) {
            return { ok: false, error: `Twilio ${resp.status}: ${(data?.message as string) || JSON.stringify(data).slice(0, 200)}` }
        }
        return {
            ok: true,
            messageId: String(data?.sid || ''),
            status: String(data?.status || 'queued')
        }
    } catch (e) {
        return { ok: false, error: String(e) }
    }
}

// Edge function roda em container — localhost aponta pra ele mesmo, não pro host.
// Substitui localhost/127.0.0.1 por host.docker.internal pra alcançar o host.
function rewriteForContainer(apiUrl: string): string {
    try {
        const u = new URL(apiUrl)
        if (u.hostname === 'localhost' || u.hostname === '127.0.0.1') {
            u.hostname = 'host.docker.internal'
            return u.toString().replace(/\/+$/, '')
        }
        return apiUrl.replace(/\/+$/, '')
    } catch {
        return apiUrl
    }
}

Deno.serve(async (req: Request) => {
    if (req.method === 'OPTIONS') return new Response('ok', { headers: corsHeaders })
    if (req.method !== 'POST') return json({ ok: false, error: 'method' }, 405)

    try {
        const body = await req.json().catch(() => null)
        if (!body || typeof body !== 'object') return json({ ok: false, error: 'body invalido' }, 400)

        const { tenant_id, to_number, body: text, patient_id } = body as Record<string, string>
        const cleanText = sanitizeBody(text)
        const cleanPhone = normalizePhone(to_number)

        if (!tenant_id || !cleanPhone || !cleanText) {
            return json({ ok: false, error: 'faltam campos (tenant_id, to_number, body)' }, 400)
        }
        if (cleanText.length < 1) return json({ ok: false, error: 'mensagem vazia' }, 400)

        // Autenticação
        const auth = req.headers.get('Authorization') ?? ''
        const supaAuthed = createClient(
            Deno.env.get('SUPABASE_URL')!,
            Deno.env.get('SUPABASE_ANON_KEY')!,
            { global: { headers: { Authorization: auth } } }
        )
        const { data: authData, error: authErr } = await supaAuthed.auth.getUser()
        if (authErr || !authData?.user) return json({ ok: false, error: 'auth' }, 401)

        const supaSvc = adminClient()

        // Client ligado ao schema do tenant (tabelas tenant: notification_channels,
        // conversation_messages). Lança se o tenant não existir/sem schema.
        let tdb
        try {
            tdb = await tenantDbForId(supaSvc, tenant_id)
        } catch (e) {
            console.error('[send-whatsapp-message] schema indisponível:', (e as Error).message)
            return json({ ok: false, error: 'tenant_invalido' }, 400)
        }

        const userId = authData.user.id
        // Valida membership
        const { data: isAdmin } = await supaSvc.rpc('is_saas_admin')
        if (!isAdmin) {
            const { data: membership } = await supaSvc
                .from('tenant_members')
                .select('id')
                .eq('tenant_id', tenant_id)
                .eq('user_id', userId)
                .eq('status', 'active')
                .maybeSingle()
            if (!membership) return json({ ok: false, error: 'forbidden' }, 403)
        }

        // Busca canal (Evolution ou Twilio) — tabela tenant
        const { data: channel, error: chErr } = await tdb
            .from('notification_channels')
            .select('credentials, provider, twilio_subaccount_sid, twilio_phone_number, is_active')
            .eq('channel', 'whatsapp')
            .is('deleted_at', null)
            .maybeSingle()

        if (chErr || !channel) {
            return json({ ok: false, error: 'Canal WhatsApp nao configurado pra este tenant' }, 400)
        }
        if (!channel.is_active) {
            return json({ ok: false, error: 'Canal WhatsApp inativo. Verifique /configuracoes/whatsapp.' }, 400)
        }

        const provider = channel.provider  // 'evolution' ou 'twilio'

        // Resolve patient_id se não veio (match por telefone)
        let resolvedPatientId = patient_id ?? null
        if (!resolvedPatientId) {
            const { data: matchData } = await supaSvc.rpc('match_patient_by_phone', {
                p_tenant_id: tenant_id,
                p_phone: cleanPhone
            })
            resolvedPatientId = (matchData as string | null) ?? null
        }

        // ─────────────────────────────────────────────────────
        // TWILIO: deduz crédito ANTES, envia, refunda se falhar
        // ─────────────────────────────────────────────────────
        if (provider === 'twilio') {
            // Deduz 1 crédito de forma atômica (RPC lança 'insufficient_credits' se zerado)
            const { error: dedErr } = await supaSvc.rpc('deduct_whatsapp_credits', {
                p_tenant_id: tenant_id,
                p_amount: 1,
                p_conversation_message_id: null,
                p_note: 'Envio manual WhatsApp'
            })
            if (dedErr) {
                const insufficient = String(dedErr.message || '').includes('insufficient_credits')
                return json({
                    ok: false,
                    error: insufficient ? 'insufficient_credits' : dedErr.message,
                    message: insufficient
                        ? 'Saldo insuficiente. Compre créditos em /configuracoes/creditos-whatsapp.'
                        : 'Erro ao deduzir créditos'
                }, insufficient ? 402 : 500)
            }

            // Envia via Twilio
            const sendRes = await sendViaTwilio(channel as unknown as TwilioChannel, cleanPhone, cleanText)

            if (!sendRes.ok) {
                // Refunda o crédito deduzido
                await supaSvc.rpc('add_whatsapp_credits', {
                    p_tenant_id: tenant_id,
                    p_amount: 1,
                    p_kind: 'refund',
                    p_purchase_id: null,
                    p_admin_id: null,
                    p_note: `Refund envio falhou: ${sendRes.error?.slice(0, 200)}`
                })
                return json({ ok: false, error: `twilio_send_failed: ${sendRes.error}` }, 502)
            }

            // Registra mensagem + vincula a dedução (se possível — já foi feita sem msg_id)
            const { data: inserted, error: insErr } = await tdb.from('conversation_messages').insert({
                patient_id: resolvedPatientId,
                channel: 'whatsapp',
                direction: 'outbound',
                from_number: null,
                to_number: cleanPhone,
                body: cleanText,
                provider: 'twilio',
                provider_message_id: sendRes.messageId ?? null,
                provider_raw: { status: sendRes.status },
                kanban_status: 'awaiting_patient',
                received_at: null,
                responded_at: new Date().toISOString(),
                delivery_status: sendRes.status === 'delivered' ? 'delivered' : sendRes.status === 'sent' ? 'sent' : 'queued'
            }).select('id').single()

            if (insErr) {
                console.error('[send-whatsapp-message] insert error (twilio):', insErr)
                return json({ ok: false, error: 'Enviado + creditado mas falhou ao registrar: ' + insErr.message }, 500)
            }

            return json({
                ok: true,
                provider: 'twilio',
                message_id: inserted.id,
                provider_message_id: sendRes.messageId
            })
        }

        // ─────────────────────────────────────────────────────
        // EVOLUTION: sem dedução (tier gratuito)
        // ─────────────────────────────────────────────────────
        const creds = (channel.credentials ?? {}) as Record<string, string>
        const apiUrl = creds.api_url
        const apiKey = creds.api_key
        const instance = creds.instance_name

        if (!apiUrl || !apiKey || !instance) {
            return json({ ok: false, error: 'Credenciais Evolution incompletas' }, 400)
        }

        const evoEndpoint = `${rewriteForContainer(apiUrl)}/message/sendText/${encodeURIComponent(instance)}`
        const evoBody = { number: cleanPhone, text: cleanText }

        const evoResp = await fetch(evoEndpoint, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json', 'apikey': apiKey },
            body: JSON.stringify(evoBody)
        })

        const evoText = await evoResp.text()
        let evoJson: Record<string, unknown> | null = null
        try { evoJson = JSON.parse(evoText) } catch { /* fica null */ }

        if (!evoResp.ok) {
            return json({
                ok: false,
                error: `Evolution retornou ${evoResp.status}`,
                evolution_response: evoJson ?? evoText
            }, 502)
        }

        const providerMessageId = (evoJson as { key?: { id?: string } } | null)?.key?.id ?? null

        const { data: inserted, error: insErr } = await tdb.from('conversation_messages').insert({
            patient_id: resolvedPatientId,
            channel: 'whatsapp',
            direction: 'outbound',
            from_number: null,
            to_number: cleanPhone,
            body: cleanText,
            provider: 'evolution',
            provider_message_id: providerMessageId,
            provider_raw: evoJson ?? null,
            kanban_status: 'awaiting_patient',
            received_at: null,
            responded_at: new Date().toISOString(),
            delivery_status: 'sent'
        }).select('id').single()

        if (insErr) {
            console.error('[send-whatsapp-message] insert error (evolution):', insErr)
            return json({ ok: false, error: 'Enviado mas falhou ao registrar: ' + insErr.message }, 500)
        }

        return json({
            ok: true,
            provider: 'evolution',
            message_id: inserted.id,
            provider_message_id: providerMessageId,
            evolution_response: evoJson
        })
    } catch (err) {
        return json({ ok: false, error: String(err) }, 500)
    }
})
