"use client";

import { useCallback, useEffect, useMemo, useState } from "react";
import {
  API_ORIGIN,
  reportDownloadUrl,
  runReport,
} from "@/lib/api";
import { CustomerPicker } from "@/components/CustomerPicker";
import { formatMoney, formatNumber } from "@/lib/labels";
import type {
  ReportDef,
  ReportParam,
  ReportRunResult,
} from "@/lib/types";

/**
 * The shared runner. Renders the filter form, fetches the result, and
 * shows the table + output buttons. One component, every report — the
 * per-report shape comes entirely from the def the API returns.
 */
export function ReportRunner({
  def,
  initialParams,
}: {
  def: ReportDef;
  /** Pre-filled param values (e.g. when launched with a customerId from
   *  a context button on a customer detail page). */
  initialParams?: Record<string, string>;
}) {
  // The form state, keyed by param.key. Initialised from defaults +
  // initialParams (initialParams wins for explicitly-set keys).
  const [params, setParams] = useState<Record<string, string>>(() => {
    const seed: Record<string, string> = {};
    for (const p of def.params) {
      if (p.kind === "select" || p.kind === "text" || p.kind === "number" || p.kind === "date") {
        if (p.defaultValue != null) seed[p.key] = p.defaultValue;
      }
    }
    if (initialParams) Object.assign(seed, initialParams);
    return seed;
  });

  const [result, setResult] = useState<ReportRunResult | null>(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);

  const run = useCallback(
    (p: Record<string, string>) => {
      setLoading(true);
      setError(null);
      runReport(def.slug, p)
        .then(setResult)
        .catch((e) => {
          setError(e?.message ?? "No se pudo correr el reporte.");
          setResult(null);
        })
        .finally(() => setLoading(false));
    },
    [def.slug],
  );

  // Auto-run on mount so the runner opens with results, not blank.
  useEffect(() => {
    run(params);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  function updateParam(key: string, value: string) {
    setParams((prev) => ({ ...prev, [key]: value }));
  }

  function applyFilters(e?: React.FormEvent) {
    e?.preventDefault();
    run(params);
  }

  return (
    <div className="report-runner">
      <form className="report-filters" onSubmit={applyFilters}>
        {def.params.map((p) => (
          <ParamField
            key={p.key}
            param={p}
            value={params[p.key] ?? ""}
            onChange={(v) => updateParam(p.key, v)}
          />
        ))}
        <div className="report-filters-actions">
          <button
            type="submit"
            className="btn btn-primary"
            disabled={loading}
          >
            {loading ? "Corriendo…" : "Correr reporte"}
          </button>
        </div>
      </form>

      {error && <div className="report-error">{error}</div>}

      {result && (
        <ResultBlock def={def} result={result} params={params} loading={loading} />
      )}
    </div>
  );
}

/* ---------------------------------------------------------- one param field */

function ParamField({
  param,
  value,
  onChange,
}: {
  param: ReportParam;
  value: string;
  onChange: (v: string) => void;
}) {
  const label = (
    <span className="filter-label">
      {param.label}
      {param.kind === "customer-picker" && !value && (
        <span className="muted" style={{ marginLeft: 6, fontWeight: 400 }}>
          (requerido)
        </span>
      )}
    </span>
  );

  if (param.kind === "select") {
    return (
      <label className="filter-field">
        {label}
        <select
          className="input select"
          value={value}
          onChange={(e) => onChange(e.target.value)}
        >
          {!param.defaultValue && <option value="">—</option>}
          {param.options.map((o) => (
            <option key={o.value} value={o.value}>
              {o.label}
            </option>
          ))}
        </select>
      </label>
    );
  }
  if (param.kind === "date") {
    return (
      <label className="filter-field">
        {label}
        <input
          type="date"
          className="input"
          value={value}
          onChange={(e) => onChange(e.target.value)}
        />
      </label>
    );
  }
  if (param.kind === "number") {
    return (
      <label className="filter-field">
        {label}
        <input
          type="number"
          className="input"
          value={value}
          onChange={(e) => onChange(e.target.value)}
          placeholder={param.defaultValue}
        />
      </label>
    );
  }
  if (param.kind === "customer-picker") {
    return (
      <div className="filter-field">
        {label}
        <CustomerPicker
          value={value}
          onPick={(id) => onChange(id)}
        />
      </div>
    );
  }
  return (
    <label className="filter-field">
      {label}
      <input
        type="text"
        className="input"
        value={value}
        onChange={(e) => onChange(e.target.value)}
        placeholder={param.placeholder ?? ""}
      />
    </label>
  );
}

/* ---------------------------------------------------------- results block */

function ResultBlock({
  def,
  result,
  params,
  loading,
}: {
  def: ReportDef;
  result: ReportRunResult;
  params: Record<string, string>;
  loading: boolean;
}) {
  return (
    <div className="report-result">
      <div className="report-result-head">
        <div className="report-result-meta">
          {result.subtitle && <p className="muted">{result.subtitle}</p>}
          <p className="muted small">
            {formatNumber(result.rows.length)} fila
            {result.rows.length === 1 ? "" : "s"}
            {result.totals && (
              <>
                {" "}·{" "}
                {Object.entries(result.totals)
                  .map(([k, v]) => `${k}: ${v}`)
                  .join(" · ")}
              </>
            )}
          </p>
        </div>
        <div className="report-output-buttons">
          <a
            className="btn btn-outline btn-sm"
            href={reportDownloadUrl(def.slug, "print", params)}
            target="_blank"
            rel="noopener"
          >
            Imprimir
          </a>
          <a
            className="btn btn-outline btn-sm"
            href={reportDownloadUrl(def.slug, "csv", params)}
            download
          >
            CSV
          </a>
          <a
            className="btn btn-outline btn-sm"
            href={reportDownloadUrl(def.slug, "xlsx", params)}
            download
          >
            Excel
          </a>
          <a
            className="btn btn-outline btn-sm"
            href={reportDownloadUrl(def.slug, "pdf", params)}
            download
          >
            PDF
          </a>
        </div>
      </div>

      {loading && <div className="report-loading">Actualizando…</div>}

      {def.format === "statement" ? (
        <StatementLayout result={result} />
      ) : def.format === "letter" ? (
        <LetterLayout result={result} />
      ) : (
        <TabularLayout result={result} />
      )}
    </div>
  );
}

/* ---------------------------------------------------------- tabular layout */

function TabularLayout({ result }: { result: ReportRunResult }) {
  if (result.rows.length === 0) {
    return (
      <div className="empty-inline">
        No se encontraron filas con los filtros actuales.
      </div>
    );
  }
  return (
    <div className="report-table-wrap">
      <table className="report-table">
        <thead>
          <tr>
            {result.columns.map((c) => (
              <th
                key={c.key}
                style={{
                  textAlign: c.align ?? "left",
                  width: c.width ? `${c.width * 6}px` : undefined,
                }}
              >
                {c.label}
              </th>
            ))}
          </tr>
        </thead>
        <tbody>
          {result.rows.map((r, i) => (
            <tr key={i}>
              {result.columns.map((c) => {
                const v = r[c.key];
                return (
                  <td
                    key={c.key}
                    style={{
                      textAlign: c.align ?? "left",
                      fontVariantNumeric:
                        c.type === "money" || c.type === "number"
                          ? "tabular-nums"
                          : undefined,
                    }}
                  >
                    {formatCell(v, c.type)}
                  </td>
                );
              })}
            </tr>
          ))}
        </tbody>
        {result.totals && (
          <tfoot>
            <tr>
              <td
                colSpan={result.columns.length}
                className="report-totals"
              >
                {Object.entries(result.totals)
                  .map(([k, v]) => `${k}: ${v}`)
                  .join("   ·   ")}
              </td>
            </tr>
          </tfoot>
        )}
      </table>
    </div>
  );
}

function formatCell(v: unknown, type: string): string {
  if (v === null || v === undefined || v === "") return "—";
  if (type === "money") {
    const n = Number(v);
    return Number.isFinite(n)
      ? new Intl.NumberFormat("es-MX", {
          minimumFractionDigits: 2,
          maximumFractionDigits: 2,
        }).format(n)
      : String(v);
  }
  if (type === "number") {
    const n = Number(v);
    return Number.isFinite(n) ? formatNumber(n) : String(v);
  }
  return String(v);
}

/* --------------------------------------------------------- statement layout */

function StatementLayout({ result }: { result: ReportRunResult }) {
  // The edo-cuenta-datos report synthesises a list with __kind
  // discriminators (header, summary, movements-header, plain movement).
  // Group by kind and render each block inline.
  const header = result.rows.find((r) => r.__kind === "header") as
    | Record<string, unknown>
    | undefined;
  const summaries = result.rows.filter((r) => r.__kind === "summary");
  const movements = result.rows.filter(
    (r) => r.__kind !== "header" && r.__kind !== "summary" && r.__kind !== "movements-header",
  );

  if (!header) {
    return (
      <div className="empty-inline">
        Selecciona un cliente y corre el reporte para ver el estado de cuenta.
      </div>
    );
  }

  return (
    <div className="statement">
      <header className="statement-head">
        <h2 className="statement-name">{String(header.name ?? "—")}</h2>
        {Boolean(header.address) && (
          <p className="muted">{String(header.address)}</p>
        )}
        {Boolean(header.city) && <p className="muted">{String(header.city)}</p>}
        {Boolean(header.phone || header.email) && (
          <p className="muted small">
            {String(header.phone ?? "")}
            {header.phone && header.email ? " · " : ""}
            {String(header.email ?? "")}
          </p>
        )}
      </header>

      {summaries.length > 0 && (
        <section className="statement-summary">
          <h3 className="statement-section-title">Resumen por moneda</h3>
          <table className="report-table">
            <thead>
              <tr>
                <th>Moneda</th>
                <th style={{ textAlign: "right" }}>Cargos</th>
                <th style={{ textAlign: "right" }}>Abonos</th>
                <th style={{ textAlign: "right" }}>Saldo</th>
                <th style={{ textAlign: "right" }}>Movs.</th>
              </tr>
            </thead>
            <tbody>
              {summaries.map((s, i) => (
                <tr key={i}>
                  <td>{String(s.currency)}</td>
                  <td style={{ textAlign: "right", fontVariantNumeric: "tabular-nums" }}>
                    {formatCell(s.charges, "money")}
                  </td>
                  <td style={{ textAlign: "right", fontVariantNumeric: "tabular-nums" }}>
                    {formatCell(s.credits, "money")}
                  </td>
                  <td style={{ textAlign: "right", fontVariantNumeric: "tabular-nums" }}>
                    {formatCell(s.balance, "money")}
                  </td>
                  <td style={{ textAlign: "right" }}>{formatCell(s.count, "number")}</td>
                </tr>
              ))}
            </tbody>
          </table>
        </section>
      )}

      {movements.length > 0 && (
        <section className="statement-movements">
          <h3 className="statement-section-title">Movimientos</h3>
          <div className="report-table-wrap">
            <table className="report-table">
              <thead>
                <tr>
                  <th>Fecha</th>
                  <th>Concepto</th>
                  <th>Referencia</th>
                  <th style={{ textAlign: "right" }}>Cargo / Abono</th>
                  <th style={{ textAlign: "right" }}>Saldo</th>
                </tr>
              </thead>
              <tbody>
                {movements.map((m, i) => (
                  <tr key={i}>
                    <td>{String(m.date ?? "")}</td>
                    <td>{String(m.concept ?? "")}</td>
                    <td>{String(m.reference ?? "")}</td>
                    <td style={{ textAlign: "right", fontVariantNumeric: "tabular-nums" }}>
                      {formatCell(m.amount, "money")}
                    </td>
                    <td style={{ textAlign: "right", fontVariantNumeric: "tabular-nums" }}>
                      {formatCell(m.balanceAfter, "money")}
                    </td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        </section>
      )}
    </div>
  );
}

/* ----------------------------------------------------------- letter layout */

const GENERATION_LABEL: Record<string, string> = {
  "1": "1er aviso",
  "2": "2o aviso",
  "3": "3er aviso",
};

interface LetterVehicle {
  make?: string | null;
  model?: string | null;
  modelYear?: string | number | null;
  bodyType?: string | null;
  engineNumber?: string | null;
  licensePlate?: string | null;
}

/**
 * One card per policy due for renewal — the parameterized replacement for
 * the ~40 cloned "AVISO DE RENOVACION" Access reports (see
 * docs/RENEWAL_NOTICES.md). Coverage figures (CSL limit, medical coverage,
 * etc.) come from data (`aviso-renovacion`'s ReportDef reads
 * `Policy.coveragesJson`) instead of the legacy's hand-typed label text, so
 * one layout renders every carrier/coverage combination.
 */
function LetterLayout({ result }: { result: ReportRunResult }) {
  const letters = result.rows.filter((r) => r.__kind === "letter");

  if (letters.length === 0) {
    return (
      <div className="empty-inline">
        No hay pólizas por vencer con los filtros actuales.
      </div>
    );
  }

  return (
    <div className="renewal-letters">
      {letters.map((r, i) => {
        const vehicle = r.vehicle as LetterVehicle | null;
        const generation = String(r.generation ?? "1");
        return (
          <article className="renewal-letter" key={String(r.policyId ?? i)}>
            <header className="renewal-letter-head">
              <div>
                <h2 className="renewal-letter-title">Aviso de renovación</h2>
                <p className="muted small">
                  {GENERATION_LABEL[generation] ?? `Aviso ${generation}`}
                </p>
              </div>
              <div className="renewal-letter-status">
                {r.sentAt ? (
                  <span className="badge badge-positive">
                    <span className="dot" />
                    Enviado {String(r.sentAt)}
                  </span>
                ) : (
                  <span className="badge badge-neutral">
                    <span className="dot" />
                    Pendiente
                  </span>
                )}
              </div>
            </header>

            <div className="renewal-letter-grid">
              <div>
                <p className="muted small">Cliente</p>
                <p className="renewal-letter-value">
                  {String(r.customerName ?? "—")}
                </p>
              </div>
              <div>
                <p className="muted small">Póliza</p>
                <p className="renewal-letter-value">
                  {String(r.policyNumber ?? "—")}
                </p>
              </div>
              <div>
                <p className="muted small">Aseguradora</p>
                <p className="renewal-letter-value">
                  {String(r.provider ?? "—")}
                </p>
              </div>
              <div>
                <p className="muted small">Vence</p>
                <p className="renewal-letter-value">
                  {String(r.policyTo ?? "—")}
                </p>
              </div>
            </div>

            {vehicle && (
              <div className="renewal-letter-vehicle">
                <p className="muted small">Vehículo asegurado</p>
                <p className="renewal-letter-value">
                  {[vehicle.modelYear, vehicle.make, vehicle.model]
                    .filter(Boolean)
                    .join(" ")}
                  {vehicle.bodyType ? ` · ${vehicle.bodyType}` : ""}
                </p>
                <p className="muted small">
                  {[
                    vehicle.engineNumber && `Motor: ${vehicle.engineNumber}`,
                    vehicle.licensePlate && `Placa: ${vehicle.licensePlate}`,
                  ]
                    .filter(Boolean)
                    .join(" · ")}
                </p>
              </div>
            )}

            <div className="renewal-letter-coverage">
              <CoverageItem label="Cobertura (días)" value={r.coverageDays} />
              <CoverageItem label="CSL" value={r.cslLimit} money />
              <CoverageItem label="Gastos médicos" value={r.medicalCoverage} money />
              <CoverageItem label="Daños a propiedad" value={r.propertyDamage} money />
              <CoverageItem label="Responsabilidad por persona" value={r.perPersonLiability} money />
            </div>

            <div className="renewal-letter-premium">
              {Boolean(r.netPremium) && (
                <span>Prima neta: {formatCell(r.netPremium, "money")}</span>
              )}
              {Boolean(r.total) && (
                <span className="renewal-letter-total">
                  Total: {formatCell(r.total, "money")} {String(r.currency ?? "")}
                </span>
              )}
            </div>
          </article>
        );
      })}
    </div>
  );
}

function CoverageItem({
  label,
  value,
  money,
}: {
  label: string;
  value: unknown;
  money?: boolean;
}) {
  if (value === null || value === undefined || value === "") return null;
  return (
    <div className="renewal-letter-coverage-item">
      <p className="muted small">{label}</p>
      <p className="renewal-letter-value">
        {money ? formatCell(value, "money") : String(value)}
      </p>
    </div>
  );
}

// Hint to the bundler that API_ORIGIN is part of the API surface used here.
void API_ORIGIN;
