import "server-only"
import nodemailer from "nodemailer"
import { prisma } from "@/server/db"
import { activitySearch, emailSummary, viewApiKeys } from "@/server/smtp2go"
import { getClienteByCnpj, getContatos } from "@/server/erp"

type AlertThresholds = {
  bounceMin: number
  spamMin: number
  days: number
  subaccountIds?: string[]
}

const ALERT_BCC = "suporte@meile.com.br"

type AlertResult = {
  sent: number
  skipped: number
  errors: Array<{ subaccountId: string; reason: string }>
}

function csvEscape(value: unknown) {
  const str = value === null || value === undefined ? "" : String(value)
  if (/[",\n]/.test(str)) return `"${str.replace(/"/g, '""')}"`
  return str
}

function parsePercent(value: unknown) {
  if (typeof value === "number") return value
  const n = Number(String(value ?? "").replace("%", ""))
  return Number.isFinite(n) ? n : null
}

function formatPercent(value: number | null | undefined) {
  if (value === null || value === undefined) return "-"
  return `${value.toFixed(2)}%`
}

function toNumber(value: unknown) {
  const n = Number(value)
  return Number.isFinite(n) ? n : 0
}

function bouncePercentFromCounts(emails: unknown, softbounces: unknown, hardbounces: unknown) {
  const total = toNumber(emails)
  if (total <= 0) return 0
  const soft = toNumber(softbounces)
  const hard = toNumber(hardbounces)
  return ((soft + hard) / total) * 100
}

function getSmtpConfig() {
  const host = process.env.ALERT_SMTP_HOST
  const port = process.env.ALERT_SMTP_PORT
  const user = process.env.ALERT_SMTP_USER
  const pass = process.env.ALERT_SMTP_PASS
  const from = process.env.ALERT_SMTP_FROM || user
  if (!host || !port || !user || !pass || !from) {
    throw new Error("ALERT_SMTP_HOST/ALERT_SMTP_PORT/ALERT_SMTP_USER/ALERT_SMTP_PASS/ALERT_SMTP_FROM are required")
  }
  return { host, port: Number(port), user, pass, from }
}

function pickFirstEmail(contatos: Array<Record<string, unknown>>) {
  for (const c of contatos) {
    const email = typeof c.email === "string" ? c.email.trim() : ""
    if (email) return email
  }
  return null
}

function extractEventField(event: Record<string, unknown>, keys: string[]) {
  for (const key of keys) {
    if (event[key] !== undefined && event[key] !== null) return event[key]
  }
  return null
}

function normalizeClienteId(value: unknown) {
  const raw = value === null || value === undefined ? "" : String(value)
  const digits = raw.replace(/\D/g, "")
  return digits || null
}

function formatDatePtBr(value: unknown) {
  if (!value) return ""
  const d = new Date(String(value))
  if (Number.isNaN(d.getTime())) return ""
  return d.toLocaleString("pt-BR", { timeZone: "UTC" })
}

async function buildCsv(events: Array<Record<string, unknown>>) {
  const header = [
    "data_hora",
    "Remetente",
    "Destinatario",
    "Assunto",
    "Motivo",
    "Id_mensagem",
  ]

  const lines = [
    header.join(","),
    ...events.map((ev) => {
      const values = [
        formatDatePtBr(extractEventField(ev, ["time", "timestamp", "event_timestamp", "date"])),
        extractEventField(ev, ["sender", "mail_from", "from"]),
        extractEventField(ev, ["recipient", "rcpt_to", "to"]),
        extractEventField(ev, ["subject"]),
        extractEventField(ev, ["reason", "error", "smtp_response"]),
        extractEventField(ev, ["email_id", "raw_message_id", "message_id"]),
      ]
      return values.map(csvEscape).join(",")
    }),
  ]
  return lines.join("\n")
}

function alertHtml(params: {
  name: string
  bounce: number | null
  spam: number | null
  days: number
}) {
  const year = new Date().getUTCFullYear()
  return `
  <div style="margin:0;padding:0;background:#f3f4f6;">
    <div style="max-width:640px;margin:0 auto;padding:24px;">
      <div style="background:#111827;color:#ffffff;padding:20px 24px;border-radius:12px 12px 0 0;">
        <div style="font-size:18px;font-weight:700;letter-spacing:0.2px;">Meile SMTP - Alerta de Entregabilidade</div>
        <div style="font-size:13px;color:#d1d5db;margin-top:4px;">Acoes importantes e necessarias</div>
      </div>
      <div style="background:#ffffff;padding:24px;border-radius:0 0 12px 12px;border:1px solid #e5e7eb;border-top:none;">
        <div style="font-size:16px;font-weight:600;margin-bottom:12px;color:#111827;">Prezado(a) cliente ${params.name},</div>
        <div style="font-size:14px;line-height:1.6;color:#374151;margin-bottom:16px;">
          Identificamos um indice elevado de BOUNCE (rejeicao) e/ou SPAM nos envios da sua conta.
          Para manter a reputacao e a continuidade do servico, pedimos a sua atencao aos pontos abaixo.
        </div>

        <div style="display:flex;gap:12px;flex-wrap:wrap;margin-bottom:16px;">
          <div style="flex:1;min-width:180px;border:1px solid #e5e7eb;border-radius:10px;padding:12px;">
            <div style="font-size:12px;color:#6b7280;">Taxa de bounce</div>
            <div style="font-size:18px;font-weight:700;color:#111827;">${formatPercent(params.bounce)}</div>
          </div>
          <div style="flex:1;min-width:180px;border:1px solid #e5e7eb;border-radius:10px;padding:12px;">
            <div style="font-size:12px;color:#6b7280;">Taxa de spam</div>
            <div style="font-size:18px;font-weight:700;color:#111827;">${formatPercent(params.spam)}</div>
          </div>
          <div style="flex:1;min-width:180px;border:1px solid #e5e7eb;border-radius:10px;padding:12px;">
            <div style="font-size:12px;color:#6b7280;">Periodo</div>
            <div style="font-size:14px;font-weight:600;color:#111827;">Ultimos ${params.days} dias (UTC)</div>
          </div>
        </div>

        <div style="font-size:14px;line-height:1.6;color:#374151;margin-bottom:16px;">
          Em anexo, enviamos um relatorio com os destinatarios que apresentaram erros de entrega nos ultimos ${params.days} dias.
          Recomendamos a limpeza e validacao da sua lista de contatos.
        </div>

        <div style="font-size:14px;line-height:1.7;color:#374151;margin-bottom:16px;">
          E muito importante que o relatorio seja analisado com atencao, pois ele ajuda a identificar possiveis problemas
          que podem afetar a entregabilidade dos e-mails e a reputacao do seu dominio.
        </div>

        <div style="background:#f9fafb;border:1px solid #e5e7eb;border-radius:10px;padding:16px;margin-bottom:16px;">
          <div style="font-size:13px;font-weight:700;color:#111827;margin-bottom:10px;">Como interpretar os tipos de falha</div>
          <div style="font-size:13px;line-height:1.7;color:#374151;margin-bottom:10px;">
            <strong style="color:#111827;">Soft Bounce:</strong> as causas comuns podem ser caixa de correio cheia,
            indisponibilidade temporaria do provedor de e-mail, alta carga no servidor destinatario ou problemas com filtros de spam.
          </div>
          <div style="font-size:13px;line-height:1.7;color:#374151;margin-bottom:10px;">
            <strong style="color:#111827;">Hard Bounce:</strong> as causas comuns podem ser endereco invalido ou inexistente,
            problemas de autenticacao (SPF/DKIM/DMARC) ou bloqueios relacionados a filtragem de spam e reputacao.
          </div>
          <div style="font-size:13px;line-height:1.7;color:#374151;">
            <strong style="color:#111827;">Rejected:</strong> o e-mail nao foi enviado porque o endereco do destinatario estava
            na lista de supressoes do SMTP, normalmente por bounce anterior, reclamacao de spam ou cancelamento de inscricao.
          </div>
        </div>

        <div style="font-size:14px;line-height:1.7;color:#374151;margin-bottom:16px;">
          Fazemos essa classificacao analisando a mensagem de erro enviada pelo servidor de e-mail do destinatario.
          Se a mensagem indicar que nao ha possibilidade de o endereco funcionar no futuro, ela e classificada como
          rejeicao permanente (hard bounce).
        </div>

        <div style="font-size:14px;line-height:1.7;color:#374151;margin-bottom:16px;">
          Um e-mail que foi rejeitado, seja permanentemente ou temporariamente, nao sera reenviado. A rejeicao e o
          resultado final para o e-mail.
        </div>

        <div style="background:#fff7ed;border:1px solid #fdba74;border-radius:10px;padding:16px;margin-bottom:16px;">
          <div style="font-size:13px;font-weight:700;color:#9a3412;margin-bottom:8px;">Importante</div>
          <div style="font-size:13px;line-height:1.7;color:#7c2d12;">
            Enderecos marcados como hard bounce ou rejected devem ser removidos das suas listas de envio para evitar
            novos bloqueios e proteger a reputacao do dominio.
          </div>
        </div>

        <div style="background:#f9fafb;border:1px solid #e5e7eb;border-radius:10px;padding:16px;margin-bottom:16px;">
          <div style="font-size:13px;font-weight:700;color:#111827;margin-bottom:8px;">Diretrizes de Uso Aceitavel</div>
          <ul style="padding-left:18px;margin:0;color:#374151;font-size:13px;line-height:1.6;">
            <li>Envie apenas para destinatarios com relacionamento previo.</li>
            <li>Evite listas compradas ou obtidas de forma indiscriminada.</li>
            <li>Mantenha listas atualizadas e com opt-out funcional.</li>
            <li>Contatos devem ter recebido mensagens nos ultimos 3 meses.</li>
          </ul>
        </div>

        <div style="font-size:14px;line-height:1.6;color:#374151;">
          Caso tenham duvidas sobre o relatorio ou sobre os envios, nossa equipe esta a disposicao para auxilia-los.
        </div>

        <div style="margin-top:20px;font-size:14px;color:#111827;">
          Atenciosamente,<br/>
        </div>
        <div style="margin-top:16px;">
          <a href="https://linktr.ee/meilebr?utm_source=linktree_profile_share&ltsid=e5ac814c-75e5-4156-a411-5a3c6473d22b">
            <img src="https://meile.com.br/an/Suporte.png" height="172.26" style="display:block;border:0;outline:none;text-decoration:none;" />
          </a>
        </div>
      </div>
      <div style="text-align:center;font-size:11px;color:#9ca3af;margin-top:12px;">
        Meile SMTP - ${year}
      </div>
    </div>
  </div>
  `
}

async function fetchRecentErrors(apiKey: string, subaccountId: string, days: number) {
  const start = new Date(Date.now() - days * 24 * 60 * 60 * 1000).toISOString()
  const end = new Date().toISOString()
  const res = await activitySearch(
    {
      start_date: start,
      end_date: end,
      limit: 1000,
      event_types: ["soft-bounced", "hard-bounced", "rejected", "spam"],
      subaccounts: [subaccountId],
    },
    { apiKey }
  )
  return res?.events ?? []
}

async function getLatestMetrics() {
  const rows = await prisma.subaccountMetric.findMany({
    orderBy: { fetchedAt: "desc" },
  })
  const latest = new Map<string, (typeof rows)[number]>()
  for (const row of rows) {
    if (!latest.has(row.subaccountId)) {
      latest.set(row.subaccountId, row)
    }
  }
  return Array.from(latest.values())
}

export async function sendAlerts(params: AlertThresholds): Promise<AlertResult> {
  const metrics = await getLatestMetrics()
  const meta = await prisma.subaccountMeta.findMany()
  const metaMap = new Map(meta.map((m) => [m.subaccountId, m]))
  const selectedIds = new Set((params.subaccountIds ?? []).map((id) => String(id).trim()).filter(Boolean))

  const targets = metrics.filter((m) => {
    if (selectedIds.size > 0 && !selectedIds.has(m.subaccountId)) return false
    const bounce = bouncePercentFromCounts(m.emails, m.softbounces, m.hardbounces)
    const spam = m.spamPercent ?? 0
    return bounce >= params.bounceMin || spam >= params.spamMin
  })

  const { host, port, user, pass, from } = getSmtpConfig()
  const transporter = nodemailer.createTransport({
    host,
    port,
    secure: true,
    auth: { user, pass },
  })

  const result: AlertResult = { sent: 0, skipped: 0, errors: [] }

  for (const t of targets) {
    const metaEntry = metaMap.get(t.subaccountId)
    const cnpj = metaEntry?.cnpj
    if (!cnpj) {
      result.skipped += 1
      result.errors.push({ subaccountId: t.subaccountId, reason: "CNPJ_NOT_SET" })
      continue
    }

    let email: string | null = null
    try {
      const cliente = await getClienteByCnpj(cnpj)
      const resultado = cliente?.resultado as Record<string, unknown>
      const clienteId = normalizeClienteId(resultado?.cliente_id ?? resultado?.id)
      if (!clienteId) {
        result.skipped += 1
        result.errors.push({ subaccountId: t.subaccountId, reason: "ERP_CLIENT_NOT_FOUND" })
        continue
      }

      const contatos = await getContatos(clienteId)
      email = pickFirstEmail(contatos?.resultado ?? [])
      if (!email) {
        result.skipped += 1
        result.errors.push({ subaccountId: t.subaccountId, reason: "ERP_CONTACT_EMAIL_NOT_FOUND" })
        continue
      }

      const keys = await viewApiKeys({ subaccount_id: t.subaccountId })
      const apiKey = keys.find((k) => k.api_key)?.api_key
      if (!apiKey) {
        result.skipped += 1
        result.errors.push({ subaccountId: t.subaccountId, reason: "SUBACCOUNT_API_KEY_NOT_FOUND" })
        continue
      }

      const events = await fetchRecentErrors(apiKey, t.subaccountId, params.days)
      const csv = await buildCsv(events)
      const name = t.name ?? metaEntry?.name ?? t.subaccountId

      await transporter.sendMail({
        from,
        to: email,
        bcc: ALERT_BCC,
        subject: `Ação necessária: alto índice de bounce/spam - ${name}`,
        html: alertHtml({
          name,
          bounce: bouncePercentFromCounts(t.emails, t.softbounces, t.hardbounces),
          spam: t.spamPercent ?? null,
          days: params.days,
        }),
        attachments: [
          {
            filename: `erros_entrega_${t.subaccountId}.csv`,
            content: csv,
            contentType: "text/csv",
          },
        ],
      })

      await prisma.alertEmailLog.create({
        data: {
          subaccountId: t.subaccountId,
          recipient: email,
          status: "sent",
        },
      })

      result.sent += 1
    } catch (e: unknown) {
      const msg = e instanceof Error ? e.message : String(e ?? "SEND_ERROR")
      await prisma.alertEmailLog.create({
        data: {
          subaccountId: t.subaccountId,
          recipient: email ?? "unknown",
          status: "error",
          error: msg,
        },
      })
      result.errors.push({ subaccountId: t.subaccountId, reason: msg })
    }
  }

  return result
}

export async function sendTestAlert(params: {
  subaccountId: string
  days: number
  overrideEmail?: string | null
}) {
  const meta = await prisma.subaccountMeta.findUnique({
    where: { subaccountId: params.subaccountId },
  })

  const { host, port, user, pass, from } = getSmtpConfig()
  const transporter = nodemailer.createTransport({
    host,
    port,
    secure: true,
    auth: { user, pass },
  })

  const cnpj = meta?.cnpj?.trim()
  if (!cnpj) {
    throw new Error("CNPJ_NOT_SET")
  }
  const cnpjDigits = cnpj.replace(/\D/g, "")
  if (cnpjDigits.length < 14) {
    throw new Error("CNPJ_INVALID")
  }

  let email = params.overrideEmail ?? null
  if (!email) {
    let cliente
    try {
      cliente = await getClienteByCnpj(cnpj)
    } catch (e: unknown) {
      const msg = e instanceof Error ? e.message : String(e ?? "")
      if ((msg === "ERP_HTTP_404" || msg === "ERP_HTTP_400") && cnpjDigits !== cnpj) {
        cliente = await getClienteByCnpj(cnpjDigits)
      } else {
        throw e
      }
    }
    const resultado = cliente?.resultado as Record<string, unknown>
    const emailFromCliente = typeof resultado?.email === "string" ? resultado.email.trim() : ""
    const clienteId = normalizeClienteId(resultado?.cliente_id ?? resultado?.id)
    if (!clienteId) {
      throw new Error("ERP_CLIENT_NOT_FOUND")
    }

    const contatos = await getContatos(clienteId)
    email = emailFromCliente || pickFirstEmail(contatos?.resultado ?? [])
  }

  if (!email) {
    throw new Error("ERP_CONTACT_EMAIL_NOT_FOUND")
  }

  const keys = await viewApiKeys({ subaccount_id: params.subaccountId })
  const apiKey = keys.find((k) => k.api_key)?.api_key
  if (!apiKey) {
    throw new Error("SUBACCOUNT_API_KEY_NOT_FOUND")
  }

  const [events, summary] = await Promise.all([
    fetchRecentErrors(apiKey, params.subaccountId, params.days),
    emailSummary({}, { apiKey }),
  ])
  const csv = await buildCsv(events)
  const name = meta?.name ?? params.subaccountId
  const summaryObj = summary as Record<string, unknown>
  const bounce = bouncePercentFromCounts(
    summaryObj?.email_count,
    summaryObj?.softbounces,
    summaryObj?.hardbounces
  )
  const spam = parsePercent((summary as Record<string, unknown>)?.spam_percent)

  await transporter.sendMail({
    from,
    to: email,
    bcc: ALERT_BCC,
    subject: `TESTE - Alerta de bounce/spam - ${name}`,
    html: alertHtml({
      name,
      bounce,
      spam,
      days: params.days,
    }),
    attachments: [
      {
        filename: `erros_entrega_${params.subaccountId}.csv`,
        content: csv,
        contentType: "text/csv",
      },
    ],
  })

  await prisma.alertEmailLog.create({
    data: {
      subaccountId: params.subaccountId,
      recipient: email,
      status: "test_sent",
    },
  })

  return { ok: true, to: email, events: events.length }
}
