"use client";

import { useCallback, useEffect, useState } from "react";
import { useCan } from "@/lib/abilities";
import { formatDate, formatMoney } from "@/lib/labels";
import { NotificationLogPanel } from "@/components/NotificationLogPanel";
import { apiFetch, POLIZAS_LOG_SCOPE, type NotificationFlags } from "@/lib/api";

/**
 * Renewal notices — the "Pólizas" half of /notificaciones. Shows which
 * renewal letters are pending in a window and lets staff send them, either
 * one row at a time or as a whole sweep. Sending is what marks a notice as
 * delivered — there is no manual "mark as sent", so the list can never claim
 * a letter went out when no mail was ever sent. Gated on `renewal:send`.
 *
 * Sends are recorded in the same `email_notification_log` the Servicios tab
 * reads, so "Registro de envíos" below is the same component with the
 * POLICIES slice — failures and no-email skips included, which the pending
 * list alone cannot show.
 *
 * `debug` comes from the shared flags card above the tabs and means the same
 * thing here as it does for servicios: the mail is diverted to the override
 * inbox. It additionally does NOT mark the notice as sent, so a test send
 * leaves the row exactly where it was — pending.
 */

export interface RenewalLetter {
  policyId: string;
  policyNumber: string;
  policyType: string;
  customerName: string;
  customerEmail: string | null;
  provider: string;
  policyTo: string;
  netPremium: string | null;
  total: string | null;
  currency: string;
  generation: number;
  sentAt: string | null;
}

export interface RenewalSweepResult {
  eligible: number;
  sent: number;
  skipped: number;
  failed: number;
  failures: { policyId: string; generation: number; error: string }[];
  debug: boolean;
}

export interface RenewalSendResult {
  policyId: string;
  generation: number;
  /** Where the mail actually went — the override inbox under debug. */
  to: string;
  debug: boolean;
  sentAt: string;
  providerMessageId?: string;
}

const GENERATION_LABEL: Record<number, string> = {
  1: "Primer aviso (30 días antes)",
  2: "Segundo aviso (15 días antes)",
  3: "Tercer aviso (7 días después)",
};

export function NotificacionesPolizas({ flags }: { flags: NotificationFlags }) {
  const allowed = useCan("renewal:send");
  const debug = !!flags.debug;
  const [days, setDays] = useState(30);
  const [pending, setPending] = useState<RenewalLetter[] | null>(null);
  const [pendingError, setPendingError] = useState<string | null>(null);
  const [actionError, setActionError] = useState<string | null>(null);
  const [notice, setNotice] = useState<string | null>(null);
  const [sweeping, setSweeping] = useState(false);
  /** `policyId-generation` of the row currently being sent, if any. */
  const [sendingKey, setSendingKey] = useState<string | null>(null);
  /** Raised after every send so the log panel reloads. */
  const [logToken, setLogToken] = useState(0);

  const refresh = useCallback(async () => {
    setPendingError(null);
    try {
      const data = await apiFetch<RenewalLetter[]>(
        `/renewals/pending?days=${days}`,
      );
      setPending(data);
    } catch (e) {
      setPendingError(
        (e as Error)?.message ?? "No se pudo cargar la lista de avisos.",
      );
      setPending([]);
    }
  }, [days]);

  useEffect(() => {
    if (allowed) refresh();
  }, [allowed, refresh]);

  async function handleSweep() {
    // Only worth confirming when debug is off — that is the case where real
    // customers receive mail. Mirrors "Ejecutar todos" on the servicios tab.
    if (!debug) {
      const ok = window.confirm(
        "debug está desactivado: los avisos irán a los correos reales de los clientes. ¿Ejecutar el barrido?",
      );
      if (!ok) return;
    }
    setActionError(null);
    setNotice(null);
    setSweeping(true);
    try {
      const result = await apiFetch<RenewalSweepResult>("/renewals/sweep", {
        method: "POST",
        body: JSON.stringify({ debug }),
      });
      setNotice(
        `Enviados ${result.sent} avisos (${result.failed} con error).` +
          (result.debug
            ? " Modo debug: fueron al buzón de pruebas y siguen pendientes."
            : ""),
      );
      setLogToken((t) => t + 1);
      await refresh();
    } catch (e) {
      setActionError((e as Error)?.message ?? "No se pudo ejecutar el barrido.");
      setLogToken((t) => t + 1);
    } finally {
      setSweeping(false);
    }
  }

  /**
   * Send this one notice now. The API records it as sent on success, so the
   * row leaves the pending list — that disappearance IS the "sent" signal,
   * backed by the confirmation line above the table.
   */
  async function handleSend(letter: RenewalLetter) {
    setActionError(null);
    setNotice(null);
    setSendingKey(`${letter.policyId}-${letter.generation}`);
    try {
      const result = await apiFetch<RenewalSendResult>("/renewals/send", {
        method: "POST",
        body: JSON.stringify({
          policyId: letter.policyId,
          generation: letter.generation,
          debug,
        }),
      });
      setNotice(
        result.debug
          ? `Prueba enviada a ${result.to}. El aviso sigue pendiente: el cliente no ha recibido nada.`
          : `Aviso enviado a ${result.to}.`,
      );
      setLogToken((t) => t + 1);
      await refresh();
    } catch (e) {
      setActionError((e as Error)?.message ?? "No se pudo enviar el aviso.");
      // A rejected send may still have written a FAILED row; reload either way.
      setLogToken((t) => t + 1);
    } finally {
      setSendingKey(null);
    }
  }

  if (!allowed) {
    return (
      <div className="empty-inline">
        No tiene permisos para enviar avisos de renovación.
      </div>
    );
  }

  const counts = (pending ?? []).reduce<Record<number, number>>(
    (acc, item) => ({
      ...acc,
      [item.generation]: (acc[item.generation] ?? 0) + 1,
    }),
    {},
  );
  const grouped = [1, 2, 3].filter((gen) => (counts[gen] ?? 0) > 0);

  return (
    <div style={{ display: "grid", gap: 20 }}>
      <p className="muted" style={{ maxWidth: 760, margin: 0 }}>
        El sistema ejecuta un barrido automático (ver «Programación de envíos»
        arriba) que notifica a los clientes a 30, 15 y 7 días antes o después
        del vencimiento de su póliza. Esta sección muestra qué avisos están
        pendientes y permite ejecutarlo manualmente.
      </p>

      {actionError && <div className="state-box state-error">{actionError}</div>}
      {notice && <div className="empty-inline">{notice}</div>}

      <section className="card" style={{ padding: 20 }}>
        <div className="row-actions" style={{ justifyContent: "space-between" }}>
          <div>
            <h2 className="section-title">Barrido manual</h2>
            <p className="muted small" style={{ marginTop: 4 }}>
              Usa la fecha actual del servidor como referencia para seleccionar
              avisos vencidos a 30 y 15 días, y vencidos hace 7 días.
            </p>
          </div>
          <button
            type="button"
            className="btn btn-primary"
            disabled={sweeping}
            onClick={handleSweep}
          >
            {sweeping ? "Enviando…" : "Ejecutar barrido"}
          </button>
        </div>
        <div className="field" style={{ maxWidth: 180, marginTop: 12, marginBottom: 0 }}>
          <span className="field-label">Ventana (días)</span>
          <input
            className="input"
            type="number"
            min={1}
            max={365}
            value={days}
            onChange={(e) =>
              setDays(Math.min(365, Math.max(1, Number(e.target.value) || 30)))
            }
          />
        </div>
      </section>

      {pendingError && <div className="state-box state-error">{pendingError}</div>}

      {!pendingError && grouped.length === 0 && (
        <div className="empty-inline">
          No hay avisos pendientes en esta ventana.
        </div>
      )}

      {grouped.map((generation) => (
        <section className="card" key={generation} style={{ padding: 20 }}>
          <h2 className="section-title">{GENERATION_LABEL[generation]}</h2>
          <div className="tx-scroll" style={{ marginTop: 12 }}>
            <table className="tx-table">
              <thead>
                <tr>
                  <th>Cliente</th>
                  <th>Póliza</th>
                  <th>Tipo</th>
                  <th>Aseguradora</th>
                  <th>Vence</th>
                  <th className="num">Prima</th>
                  <th>Acciones</th>
                </tr>
              </thead>
              <tbody>
                {(pending ?? [])
                  .filter((item) => item.generation === generation)
                  .map((item) => (
                    <tr key={`${item.policyId}-${item.generation}`}>
                      <td>
                        <div>{item.customerName}</div>
                        <div className="muted small">
                          {item.customerEmail ?? "Sin correo"}
                        </div>
                      </td>
                      <td className="mono">{item.policyNumber}</td>
                      <td>{item.policyType}</td>
                      <td>{item.provider}</td>
                      <td>{formatDate(item.policyTo)}</td>
                      <td className="num">
                        {formatMoney(item.total ?? item.netPremium, item.currency)}
                      </td>
                      <td>
                        <div className="row-actions">
                          <button
                            type="button"
                            className="btn btn-outline btn-sm"
                            onClick={() => handleSend(item)}
                            disabled={
                              !item.customerEmail ||
                              sweeping ||
                              sendingKey !== null
                            }
                          >
                            {sendingKey ===
                            `${item.policyId}-${item.generation}`
                              ? "Enviando…"
                              : "Enviar aviso"}
                          </button>
                        </div>
                      </td>
                    </tr>
                  ))}
              </tbody>
            </table>
          </div>
        </section>
      ))}

      <NotificationLogPanel
        servicio={POLIZAS_LOG_SCOPE}
        reloadToken={logToken}
        emptyHint="Todavía no se ha enviado ningún aviso de renovación con este filtro."
      />
    </div>
  );
}
