/*
|--------------------------------------------------------------------------
| Agência PSI — Edge Function: process-email-queue
|--------------------------------------------------------------------------
| Processa a notification_queue para channel = 'email'.
|
| Fluxo por item:
|   1. Busca pendentes (channel='email', status='pendente', scheduled_at <= now)
|   2. Marca como 'processando' (lock otimista)
|   3. Busca canal SMTP em notification_channels
|   4. Resolve template: COALESCE(tenant, global)
|   5. Renderiza variáveis e condicionais {{#if}}
|   6. Envia via SMTP (Deno raw TCP com STARTTLS)
|   7. Atualiza queue e insere em notification_logs
|   8. Em erro: retry com backoff ou marca 'falhou'
|--------------------------------------------------------------------------
*/

import { createClient } from 'https://esm.sh/@supabase/supabase-js@2'
import { SmtpClient } from 'https://deno.land/x/smtp@v0.7.0/mod.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 resolveVariable(key: string, vars: Record<string, unknown>): unknown {
  if (!key.includes('.')) return vars[key]
  return key.split('.').reduce((obj: any, part) => obj?.[part], vars)
}

function renderTemplate(template: string, variables: Record<string, unknown>): string {
  if (!template) return ''
  let result = template

  // Blocos condicionais {{#if var}}...{{/if}}
  result = result.replace(
    /\{\{#if\s+([\w.]+)\}\}([\s\S]*?)\{\{\/if\}\}/g,
    (_, key, content) => resolveVariable(key, variables) ? content : ''
  )

  // Substituições simples {{variavel}}
  result = result.replace(/\{\{([\w.]+)\}\}/g, (_, key) => {
    const value = resolveVariable(key, variables)
    return value !== undefined && value !== null ? String(value) : ''
  })

  return result
}

function stripHtml(html: string): string {
  return html
    .replace(/<br\s*\/?>/gi, '\n')
    .replace(/<\/p>/gi, '\n')
    .replace(/<[^>]+>/g, '')
    .replace(/\n{3,}/g, '\n\n')
    .trim()
}

// ── SMTP sender ────────────────────────────────────────────────

interface SmtpCredentials {
  host: string
  port: number
  username: string
  password: string
  from_email: string
  from_name?: string
  tls?: boolean
}

async function sendEmail(
  creds: SmtpCredentials,
  to: string,
  subject: string,
  bodyHtml: string,
  bodyText: string
): Promise<{ messageId?: string }> {
  const client = new SmtpClient()

  const connectConfig = {
    hostname: creds.host,
    port:     creds.port,
    username: creds.username,
    password: creds.password,
  }

  // Porta 465 = TLS direto, outras = STARTTLS
  if (creds.port === 465 || creds.tls === true) {
    await client.connectTLS(connectConfig)
  } else {
    await client.connect(connectConfig)
  }

  const fromHeader = creds.from_name
    ? `${creds.from_name} <${creds.from_email}>`
    : creds.from_email

  await client.send({
    from:    fromHeader,
    to,
    subject,
    content: bodyText,
    html:    bodyHtml,
  })

  await client.close()

  return { messageId: `${Date.now()}-${Math.random().toString(36).slice(2, 8)}` }
}

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

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

  const supabase = createClient(
    Deno.env.get('SUPABASE_URL')!,
    Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!
  )

  const now = new Date().toISOString()

  // 1. Busca itens pendentes de email
  const { data: items, error: fetchErr } = await supabase
    .from('notification_queue')
    .select('*')
    .eq('channel', 'email')
    .eq('status', 'pendente')
    .lte('scheduled_at', now)
    .lt('attempts', 5) // respeita max_attempts padrão
    .order('scheduled_at', { ascending: true })
    .limit(20)

  if (fetchErr) {
    return new Response(
      JSON.stringify({ error: fetchErr.message }),
      { status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
    )
  }

  if (!items || items.length === 0) {
    return new Response(
      JSON.stringify({ message: 'Nenhum email na fila', processed: 0 }),
      { status: 200, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
    )
  }

  const results: Array<{ id: string; status: string; error?: string }> = []

  for (const item of items) {
    // 2. Lock otimista — marca como processando
    const { error: lockErr } = await supabase
      .from('notification_queue')
      .update({ status: 'processando', attempts: item.attempts + 1 })
      .eq('id', item.id)
      .eq('status', 'pendente') // garante que outro worker não pegou

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

    try {
      // 3. Busca canal SMTP
      const { data: channel, error: chErr } = await supabase
        .from('notification_channels')
        .select('credentials, sender_address, provider')
        .eq('owner_id', item.owner_id)
        .eq('channel', 'email')
        .eq('is_active', true)
        .is('deleted_at', null)
        .single()

      if (chErr || !channel) {
        throw new Error('Canal SMTP não encontrado ou inativo para este owner')
      }

      // 4. Resolve template: tenant → global fallback
      const { data: tenantTpl } = await supabase
        .from('email_templates_tenant')
        .select('subject, body_html, body_text, enabled')
        .eq('tenant_id', item.tenant_id)
        .eq('owner_id', item.owner_id)
        .eq('template_key', item.template_key)
        .maybeSingle()

      // Se tenant desabilitou o template → ignorar
      if (tenantTpl && tenantTpl.enabled === false) {
        await supabase
          .from('notification_queue')
          .update({ status: 'ignorado' })
          .eq('id', item.id)

        await supabase.from('notification_logs').insert({
          tenant_id:         item.tenant_id,
          owner_id:          item.owner_id,
          queue_id:          item.id,
          agenda_evento_id:  item.agenda_evento_id,
          patient_id:        item.patient_id,
          channel:           'email',
          template_key:      item.template_key,
          schedule_key:      item.schedule_key,
          recipient_address: item.recipient_address,
          status:            'failed',
          failure_reason:    'Template desabilitado pelo tenant',
          created_at:        now,
        })

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

      // Busca global
      const { data: globalTpl } = await supabase
        .from('email_templates_global')
        .select('subject, body_html, body_text')
        .eq('key', item.template_key)
        .eq('is_active', true)
        .single()

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

      // COALESCE: tenant sobrescreve global quando não-null
      const resolvedSubject  = tenantTpl?.subject   ?? globalTpl.subject
      const resolvedBodyHtml = tenantTpl?.body_html  ?? globalTpl.body_html
      const resolvedBodyText = tenantTpl?.body_text  ?? globalTpl.body_text

      // 5. Renderiza variáveis
      const vars = item.resolved_vars || {}
      const finalSubject  = renderTemplate(resolvedSubject, vars)
      const finalBodyHtml = renderTemplate(resolvedBodyHtml, vars)
      const finalBodyText = resolvedBodyText
        ? renderTemplate(resolvedBodyText, vars)
        : stripHtml(finalBodyHtml)

      // 6. Envia via SMTP
      const creds = channel.credentials as SmtpCredentials
      if (!creds.from_email && channel.sender_address) {
        creds.from_email = channel.sender_address
      }

      const sendResult = await sendEmail(
        creds,
        item.recipient_address,
        finalSubject,
        finalBodyHtml,
        finalBodyText
      )

      // 7. Sucesso — atualiza queue
      await supabase
        .from('notification_queue')
        .update({
          status:              'enviado',
          sent_at:             new Date().toISOString(),
          provider_message_id: sendResult.messageId || null,
        })
        .eq('id', item.id)

      // Insere log de sucesso
      await supabase.from('notification_logs').insert({
        tenant_id:          item.tenant_id,
        owner_id:           item.owner_id,
        queue_id:           item.id,
        agenda_evento_id:   item.agenda_evento_id,
        patient_id:         item.patient_id,
        channel:            'email',
        template_key:       item.template_key,
        schedule_key:       item.schedule_key,
        recipient_address:  item.recipient_address,
        resolved_message:   finalBodyHtml,
        resolved_vars:      vars,
        status:             'sent',
        provider:           channel.provider || 'smtp',
        provider_message_id: sendResult.messageId || null,
        sent_at:            new Date().toISOString(),
      })

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

    } catch (err) {
      // 8. Erro — retry com backoff ou falha definitiva
      const attempts = item.attempts + 1
      const maxAttempts = item.max_attempts || 5
      const isExhausted = attempts >= maxAttempts

      const retryDelay = attempts * 2 * 60 * 1000 // backoff: attempts * 2 min
      const nextRetryAt = isExhausted
        ? null
        : new Date(Date.now() + retryDelay).toISOString()

      await supabase
        .from('notification_queue')
        .update({
          status:       isExhausted ? 'falhou' : 'pendente',
          last_error:   err.message,
          next_retry_at: nextRetryAt,
        })
        .eq('id', item.id)

      // Log de falha
      await supabase.from('notification_logs').insert({
        tenant_id:         item.tenant_id,
        owner_id:          item.owner_id,
        queue_id:          item.id,
        agenda_evento_id:  item.agenda_evento_id,
        patient_id:        item.patient_id,
        channel:           'email',
        template_key:      item.template_key,
        schedule_key:      item.schedule_key,
        recipient_address: item.recipient_address,
        status:            'failed',
        failure_reason:    err.message,
        failed_at:         new Date().toISOString(),
      })

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

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

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