/*
|--------------------------------------------------------------------------
| Agência PSI — Edge Function: process-whatsapp-queue
|--------------------------------------------------------------------------
| Processa a notification_queue para channel='whatsapp' e provider='twilio'.
| Usa credenciais da SUBCONTA de cada tenant (modelo de subcontas).
|
| schema-per-tenant: notification_queue/channels/templates/logs vivem no
| schema físico de cada tenant (SEM coluna tenant_id). O cron VARRE todos os
| tenants; se vier body.tenant_id, processa só aquele (modo single).
|
| Fluxo:
|   1. Busca itens pendentes (channel='whatsapp', status='pendente')
|   2. Filtra somente tenants com provider='twilio' em notification_channels
|   3. Lock otimista (status → processando)
|   4. Resolve template (templates do schema pertencem ao tenant)
|   5. Renderiza variáveis {{var}}
|   6. Envia via Twilio usando credenciais da SUBCONTA do tenant
|   7. Atualiza queue + insere notification_logs com estimated_cost_brl
|   8. Retry com backoff em caso de erro
|--------------------------------------------------------------------------
*/

import type { SupabaseClient } from 'https://esm.sh/@supabase/supabase-js@2'
import { adminClient, listTenantSchemas, tenantDbForId, schemaForTenant } 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',
}

// ── Template renderer ──────────────────────────────────────────────────────

function renderTemplate(template: string, variables: Record<string, unknown>): string {
  if (!template) return ''
  return template.replace(/\{\{([\w.]+)\}\}/g, (_, key) => {
    const val = variables[key]
    return val !== undefined && val !== null ? String(val) : ''
  })
}

// ── Twilio WhatsApp sender (via subconta) ─────────────────────────────────

async function sendWhatsAppViaTwilio(
  subAccountSid: string,
  subAuthToken:  string,
  fromNumber:    string,
  toNumber:      string,
  body:          string
): Promise<{ sid: string; status: string }> {
  const url  = `https://api.twilio.com/2010-04-01/Accounts/${subAccountSid}/Messages.json`
  const auth = btoa(`${subAccountSid}:${subAuthToken}`)

  const params = new URLSearchParams()
  params.set('From', `whatsapp:${fromNumber}`)
  params.set('To',   `whatsapp:${toNumber}`)
  params.set('Body', body)

  const res  = await fetch(url, {
    method:  'POST',
    headers: {
      'Authorization': `Basic ${auth}`,
      'Content-Type':  'application/x-www-form-urlencoded',
    },
    body: params.toString(),
  })

  const data = await res.json()
  if (!res.ok) {
    throw new Error(data.message || `Twilio error ${res.status}: ${data.code}`)
  }

  return { sid: data.sid, status: data.status }
}

// ── Mock sender ────────────────────────────────────────────────────────────

function isMockMode(): boolean {
  const sid = Deno.env.get('TWILIO_ACCOUNT_SID') || ''
  return sid.startsWith('AC_TEST') || Deno.env.get('DEV') === 'true'
}

function mockSend(to: string, body: string): { sid: string; status: string } {
  const sid = `mock_wa_${crypto.randomUUID().slice(0, 8)}`
  console.log(`[WA MOCK] To: ${to} | Body: ${body} | SID: ${sid}`)
  return { sid, status: 'sent' }
}

// ── Processa a fila de UM tenant ───────────────────────────────────────────

type Result = { id: string; status: string; error?: string }

interface TwilioChannel {
  twilio_subaccount_sid: string
  twilio_phone_number:   string
  cost_per_message_usd:  number
  price_per_message_brl: number
  credentials:           Record<string, string>
}

async function processTenantQueue(tdb: SupabaseClient, usdBrlRate: number): Promise<Result[]> {
  const now = new Date().toISOString()

  // 1. Busca itens pendentes de WhatsApp deste tenant
  const { data: items, error: fetchErr } = await tdb
    .from('notification_queue')
    .select('*')
    .eq('channel',  'whatsapp')
    .eq('status',   'pendente')
    .lte('scheduled_at', now)
    .order('scheduled_at', { ascending: true })
    .limit(20)

  if (fetchErr) throw new Error(fetchErr.message)
  if (!items?.length) return []

  // Há exatamente um canal twilio whatsapp por tenant (no schema do tenant).
  // Resolve uma vez (lazy) e reusa.
  let channel: TwilioChannel | null | undefined = undefined
  async function getChannel(): Promise<TwilioChannel | null> {
    if (channel !== undefined) return channel
    const { data } = await tdb
      .from('notification_channels')
      .select('twilio_subaccount_sid, twilio_phone_number, cost_per_message_usd, price_per_message_brl, credentials')
      .eq('channel',   'whatsapp')
      .eq('provider',  'twilio')
      .eq('is_active', true)
      .is('deleted_at', null)
      .maybeSingle()
    channel = (data as TwilioChannel | null) ?? null
    return channel
  }

  const results: Result[] = []

  for (const item of items) {
    if (item.attempts >= (item.max_attempts || 5)) continue

    // 2. Lock otimista
    const { error: lockErr } = await tdb
      .from('notification_queue')
      .update({ status: 'processando', attempts: item.attempts + 1 })
      .eq('id',     item.id)
      .eq('status', 'pendente')

    if (lockErr) {
      results.push({ id: item.id, status: 'skip', error: 'lock failed' })
      continue
    }

    try {
      // 3. Busca canal twilio do tenant
      const ch = await getChannel()
      if (!ch?.twilio_subaccount_sid) {
        throw new Error('Tenant não tem subconta Twilio WhatsApp ativa')
      }

      const subToken = ch.credentials?.subaccount_auth_token
      if (!subToken) throw new Error('subaccount_auth_token não encontrado nas credenciais')

      // 4. Resolve template: templates do schema pertencem ao tenant.
      //    Preferimos override do owner; senão default do schema.
      let template: { body_text: string } | null = null

      const { data: ownerTpl } = await tdb
        .from('notification_templates')
        .select('body_text')
        .eq('owner_id',  item.owner_id)
        .eq('key',       item.template_key)
        .eq('channel',   'whatsapp')
        .eq('is_active', true)
        .is('deleted_at', null)
        .maybeSingle()

      if (ownerTpl) {
        template = ownerTpl
      } else {
        const { data: defaultTpl } = await tdb
          .from('notification_templates')
          .select('body_text')
          .eq('key',        item.template_key)
          .eq('channel',    'whatsapp')
          .eq('is_default', true)
          .eq('is_active',  true)
          .is('deleted_at', null)
          .maybeSingle()
        template = defaultTpl
      }

      if (!template) throw new Error(`Template WhatsApp não encontrado: ${item.template_key}`)

      // 5. Renderiza variáveis
      const vars    = item.resolved_vars || {}
      const message = renderTemplate(template.body_text, vars)

      // 6. Envia via Twilio (subconta do tenant)
      let sendResult: { sid: string; status: string }

      if (isMockMode()) {
        sendResult = mockSend(item.recipient_address, message)
      } else {
        sendResult = await sendWhatsAppViaTwilio(
          ch.twilio_subaccount_sid,
          subToken,
          ch.twilio_phone_number,
          item.recipient_address,
          message
        )
      }

      // Custo estimado em BRL
      const costBrl = (ch.cost_per_message_usd ?? 0) * usdBrlRate

      // 7. Sucesso — atualiza fila
      await tdb
        .from('notification_queue')
        .update({
          status:              'enviado',
          sent_at:             new Date().toISOString(),
          provider_message_id: sendResult.sid,
        })
        .eq('id', item.id)

      // 7b. Insere no log
      await tdb.from('notification_logs').insert({
        owner_id:            item.owner_id,
        queue_id:            item.id,
        agenda_evento_id:    item.agenda_evento_id,
        patient_id:          item.patient_id,
        channel:             'whatsapp',
        template_key:        item.template_key,
        schedule_key:        item.schedule_key,
        recipient_address:   item.recipient_address,
        resolved_message:    message,
        resolved_vars:       vars,
        status:              'sent',
        provider:            'twilio',
        provider_message_id: sendResult.sid,
        provider_status:     sendResult.status,
        estimated_cost_brl:  costBrl,
        sent_at:             new Date().toISOString(),
      })

      results.push({ id: item.id, status: 'enviado' })

    } catch (e) {
      // 8. Erro — retry com backoff exponencial
      const attempts    = item.attempts + 1
      const maxAttempts = item.max_attempts || 5
      const isExhausted = attempts >= maxAttempts
      const retryMs     = Math.min(attempts * 2 * 60 * 1000, 30 * 60 * 1000) // max 30min

      await tdb
        .from('notification_queue')
        .update({
          status:        isExhausted ? 'falhou' : 'pendente',
          last_error:    e.message,
          next_retry_at: isExhausted ? null : new Date(Date.now() + retryMs).toISOString(),
        })
        .eq('id', item.id)

      await tdb.from('notification_logs').insert({
        owner_id:          item.owner_id,
        queue_id:          item.id,
        agenda_evento_id:  item.agenda_evento_id,
        patient_id:        item.patient_id,
        channel:           'whatsapp',
        template_key:      item.template_key,
        schedule_key:      item.schedule_key,
        recipient_address: item.recipient_address,
        status:            'failed',
        provider:          'twilio',
        failure_reason:    e.message,
        failed_at:         new Date().toISOString(),
      })

      results.push({ id: item.id, status: isExhausted ? 'falhou' : 'retry', error: e.message })
    }
  }

  return results
}

// ── Main handler ──────────────────────────────────────────────────────────

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

  const admin = adminClient()
  const usdBrlRate = parseFloat(Deno.env.get('USD_BRL_RATE') ?? '5.5')

  // Modo single-tenant se body.tenant_id; senão varre todos.
  let bodyTenantId: string | null = null
  try {
    const body = await req.json()
    bodyTenantId = body?.tenant_id ?? null
  } catch {
    // sem body / body inválido → modo varredura
  }

  const results: Result[] = []
  const errors: Array<{ tenantId: string; error: string }> = []

  try {
    if (bodyTenantId) {
      const schema = await schemaForTenant(admin, bodyTenantId)
      if (!schema) {
        return new Response(
          JSON.stringify({ error: `schema indisponível para tenant ${bodyTenantId}` }),
          { status: 404, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
        )
      }
      const tdb = await tenantDbForId(admin, bodyTenantId)
      results.push(...await processTenantQueue(tdb, usdBrlRate))
    } else {
      const tenants = await listTenantSchemas(admin)
      for (const t of tenants) {
        try {
          const tdb = admin.schema(t.schema)
          results.push(...await processTenantQueue(tdb, usdBrlRate))
        } catch (e) {
          console.error(`[process-whatsapp-queue] tenant ${t.tenantId} falhou:`, e.message)
          errors.push({ tenantId: t.tenantId, error: e.message })
        }
      }
    }
  } catch (err) {
    return new Response(
      JSON.stringify({ error: err.message }),
      { status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
    )
  }

  if (!results.length && !errors.length) {
    return new Response(
      JSON.stringify({ message: 'Nenhuma mensagem WhatsApp na fila', processed: 0 }),
      { status: 200, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
    )
  }

  const sent   = results.filter(r => r.status === 'enviado').length
  const failed = results.filter(r => r.status === 'falhou').length
  const retry  = results.filter(r => r.status === 'retry').length

  return new Response(
    JSON.stringify({ processed: results.length, sent, failed, retry, details: results, tenantErrors: errors }),
    { status: 200, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
  )
})
