"use client";

import { useEffect, useMemo, useState } from "react";
import Link from "next/link";
import { AppShell } from "@/components/AppShell";
import { MovementForm } from "@/components/MovementForm";
import {
  getBillingFacets,
  getStatement,
  voidMovement,
} from "@/lib/api";
import { useCan } from "@/lib/abilities";
import {
  balancePhrase,
  balanceTone,
  directionLabel,
  domainLabel,
  formatDate,
  formatMoney,
  formatNumber,
  ledgerSourceLabel,
  SIN_NOMBRE,
  txTypeLabel,
} from "@/lib/labels";
import type {
  BillingFacets,
  LedgerCurrency,
  Statement,
  StatementMovement,
  TransactionDomain,
} from "@/lib/types";

/**
 * One customer's statement across both business lines — the payoff of plan
 * step 6 and, ultimately, of the whole unified-customer project: a utility
 * charge and an insurance payment finally sit on the same page, under the same
 * person, with a running balance.
 *
 * The running balance is per currency, so the movement table is scoped to one
 * currency at a time — a column that alternated between pesos and dollars would
 * be a meaningless number.
 *
 * Like the legacy EDO CUENTA report, the table covers the current year only and
 * runs oldest-first, opening on the balance carried in from before it.
 */
export default function EstadoCuentaDetailPage({
  params,
}: {
  params: { id: string };
}) {
  const { id } = params;
  return (
    <AppShell>
      <StatementView id={id} />
    </AppShell>
  );
}

function StatementView({ id }: { id: string }) {
  const canCapture = useCan("ledger:create");
  const canVoid = useCan("ledger:void");
  const [data, setData] = useState<Statement | null>(null);
  const [facets, setFacets] = useState<BillingFacets | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [captureOpen, setCaptureOpen] = useState(false);

  const [currency, setCurrency] = useState<LedgerCurrency | null>(null);
  const [domain, setDomain] = useState<TransactionDomain | "">("");

  function reload() {
    let alive = true;
    setLoading(true);
    setError(null);
    getStatement(id)
      .then((d) => {
        if (!alive) return;
        setData(d);
        // Default to the currency the customer actually moves the most in;
        // preserve a previously-chosen currency across reloads.
        const busiest = [...d.summary].sort((a, b) => b.count - a.count)[0];
        setCurrency((prev) => prev ?? busiest?.currency ?? "MXN");
        setLoading(false);
      })
      .catch((e) => {
        if (!alive) return;
        setError(
          e?.status === 404
            ? "No encontramos este cliente."
            : e?.message ?? "No se pudo cargar el estado de cuenta.",
        );
        setLoading(false);
      });
    return () => {
      alive = false;
    };
  }

  useEffect(() => {
    const cleanup = reload();
    getBillingFacets().then(setFacets).catch(() => setFacets(null));
    return cleanup;
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [id]);

  const movements = useMemo(() => {
    if (!data || !currency) return [];
    return data.movements.filter(
      (m) => m.currency === currency && (!domain || m.domain === domain),
    );
  }, [data, currency, domain]);

  if (loading) return <StatementSkeleton />;

  if (error)
    return (
      <>
        <BackLink />
        <div className="state-error" role="alert">
          {error}
        </div>
      </>
    );

  if (!data || !currency) return null;

  const active = data.summary.find((s) => s.currency === currency);

  return (
    <div className="rise">
      <BackLink />
      <Hero data={data} />

      <section className="section">
        <SectionHead rule="cuenta" title="Saldo por moneda" />
        {data.summary.length === 0 ? (
          <div className="card">
            <div className="empty-inline">
              Este cliente no tiene movimientos registrados.
            </div>
          </div>
        ) : (
          <div className="summary-grid">
            {data.summary.map((s) => {
              const tone = balanceTone(s.balance);
              return (
                <button
                  type="button"
                  key={s.currency}
                  className={`summary-card bal-card ${tone}${
                    currency === s.currency ? " selected" : ""
                  }`}
                  onClick={() => setCurrency(s.currency)}
                  aria-pressed={currency === s.currency}
                >
                  <div className="summary-domain">
                    Saldo en {s.currency} · {balancePhrase(s.balance)}
                  </div>
                  <div className={`summary-total bal-amount ${tone}`}>
                    {formatMoney(s.balance, s.currency)}
                  </div>
                  <div className="bal-breakdown">
                    <span className="tx-amount neg">
                      {formatMoney(s.charges, s.currency)}
                    </span>
                    <span className="bal-breakdown-label">
                      {formatNumber(s.chargeCount)} cargos
                    </span>
                    <span className="tx-amount pos">
                      {formatMoney(s.credits, s.currency)}
                    </span>
                    <span className="bal-breakdown-label">
                      {formatNumber(s.creditCount)} abonos
                    </span>
                  </div>
                  <div className="summary-count">
                    {formatDate(s.firstMovement)} a {formatDate(s.lastMovement)}
                  </div>
                </button>
              );
            })}
          </div>
        )}
        <p className="section-note">
          Los saldos se muestran por separado en cada moneda. La contabilidad
          heredada registró los cargos únicamente en pesos y los recibos en
          ambas monedas, sin guardar el tipo de cambio aplicado a cada
          movimiento, por lo que sumarlas produciría una cifra que nunca existió
          en los libros.
        </p>
      </section>

      <PorLineaSection data={data} currency={currency} />
      <ConceptosSection data={data} currency={currency} />

      <section className="section">
        <SectionHead
          rule="cuenta"
          title={`Movimientos ${data.year}`}
          count={movements.length}
          countSuffix={movements.length === 1 ? "movimiento" : "movimientos"}
          right={
            canCapture ? (
              <button
                type="button"
                className="btn btn-primary"
                onClick={() => setCaptureOpen((v) => !v)}
              >
                {captureOpen ? "Cerrar captura" : "Capturar movimiento"}
              </button>
            ) : undefined
          }
        />

        {captureOpen && (
          <MovementForm
            concepts={facets?.types ?? []}
            defaultCurrency={currency ?? "MXN"}
            defaultCustomer={{
              id: data.customer.id,
              name: data.customer.name,
            }}
            onSaved={() => {
              setCaptureOpen(false);
              reload();
            }}
            onCancel={() => setCaptureOpen(false)}
          />
        )}

        <div className="filter-row">
          <label className="filter-field">
            <span className="filter-label">Moneda</span>
            <select
              className="input select"
              value={currency}
              onChange={(e) => setCurrency(e.target.value as LedgerCurrency)}
            >
              {data.summary.map((s) => (
                <option key={s.currency} value={s.currency}>
                  {s.currency} ({formatNumber(s.count)})
                </option>
              ))}
            </select>
          </label>
          <label className="filter-field">
            <span className="filter-label">Línea de negocio</span>
            <select
              className="input select"
              value={domain}
              onChange={(e) =>
                setDomain(e.target.value as TransactionDomain | "")
              }
            >
              <option value="">Ambas líneas</option>
              <option value="UTILITY">Servicios</option>
              <option value="INSURANCE">Seguros</option>
            </select>
          </label>
        </div>

        <div className="card">
          {movements.length === 0 ? (
            <div className="empty-inline">
              Sin movimientos de {data.year} en {currency}
              {domain ? ` para ${domainLabel(domain)}` : ""}.
            </div>
          ) : (
            <div className="tx-scroll">
              <table className="tx-table">
                <thead>
                  <tr>
                    <th>Fecha</th>
                    <th>Línea</th>
                    <th>Concepto</th>
                    <th>Referencia</th>
                    <th className="num">Cargo / Abono</th>
                    <th className="num">Saldo</th>
                    {canVoid && (
                      <th style={{ width: 1, whiteSpace: "nowrap" }}>
                        Acciones
                      </th>
                    )}
                  </tr>
                </thead>
                <tbody>
                  {/*
                    The carried balance, shown the way the legacy report shows
                    it: a BALANCE FORWARD line above the year's movements. It
                    only appears when there is something to carry — when the
                    customer's opening-balance row is itself dated inside this
                    year (the usual case) it is listed as an ordinary movement
                    and this row is zero, so it is left out.

                    Suppressed under a business-line filter: the carried balance
                    is the customer's, across both lines, and printing it above
                    one line's rows would read as that line's opening balance.
                  */}
                  {!domain && Number(active?.opening ?? 0) !== 0 && (
                    <OpeningRow
                      opening={active!.opening}
                      currency={currency}
                      year={data.year}
                      canVoid={canVoid}
                    />
                  )}
                  {movements.map((m) => (
                    <StatementRow
                      key={m.id}
                      m={m}
                      canVoid={canVoid}
                      onVoided={reload}
                    />
                  ))}
                </tbody>
              </table>
            </div>
          )}
          {domain && movements.length > 0 && (
            <div className="section-note" style={{ padding: "0 16px 14px" }}>
              La columna de saldo es el saldo acumulado del cliente en{" "}
              {currency} sobre <strong>todas</strong> sus líneas — filtrar por
              línea oculta filas, no las descuenta.
            </div>
          )}
        </div>
        {active && (
          <p className="section-note">
            Saldo final en {currency}:{" "}
            <strong>{formatMoney(active.balance, currency)}</strong> (
            {balancePhrase(active.balance).toLowerCase()}).
          </p>
        )}
      </section>
    </div>
  );
}

function BackLink() {
  return (
    <Link href="/estado-cuenta" className="back-link">
      ← Volver a Estado de cuenta
    </Link>
  );
}

function Hero({ data }: { data: Statement }) {
  const c = data.customer;
  const location = [c.city?.replace(/,\s*$/, ""), c.state]
    .filter(Boolean)
    .join(", ");

  const facts: { label: string; value: string }[] = [
    { label: "Cliente desde", value: formatDate(c.customerSince) },
    { label: "Propiedades", value: String(c.propertyCount) },
    { label: "Pólizas", value: String(c.policyCount) },
    { label: "Teléfono", value: c.phone || c.mobile || "—" },
    { label: "Correo", value: c.email || "—" },
  ];

  return (
    <div className="detail-hero">
      <div className="hero-top">
        <div>
          <h1
            className={`hero-name${
              c.name === SIN_NOMBRE ? " hero-name-missing" : ""
            }`}
          >
            {c.name}
          </h1>
          {location && <div className="hero-provenance">{location}</div>}
          {c.nameSource && (
            <div className="hero-provenance">
              Nombre recuperado de {c.nameSource} — el registro original no
              tenía nombre.
            </div>
          )}
        </div>
        <div className="hero-badges">
          {c.propertyCount > 0 && (
            <span className="badge badge-servicios">
              <span className="dot" /> Servicios
            </span>
          )}
          {c.policyCount > 0 && (
            <span className="badge badge-seguros">
              <span className="dot" /> Seguros
            </span>
          )}
          <span className={`badge ${c.status ? "badge-on-dark" : "badge-negative"}`}>
            {c.status ? "Activo" : "Inactivo"}
          </span>
        </div>
      </div>
      <div className="hero-facts">
        {facts.map((f) => (
          <div key={f.label}>
            <div className="hero-fact-label">{f.label}</div>
            <div className="hero-fact-value">{f.value}</div>
          </div>
        ))}
      </div>
      <div className="hero-links">
        <Link href={`/clientes/${c.id}`} className="btn btn-outline">
          Ver ficha del cliente
        </Link>
      </div>
    </div>
  );
}

/** The cross-line split — the same balance, broken out by business line. */
function PorLineaSection({
  data,
  currency,
}: {
  data: Statement;
  currency: LedgerCurrency;
}) {
  const rows = data.byDomain.filter((d) => d.currency === currency);
  if (rows.length === 0) return null;

  return (
    <section className="section">
      <SectionHead rule="cuenta" title={`Por línea de negocio · ${currency}`} />
      <div className="summary-grid">
        {rows.map((r) => (
          <div className={`summary-card ${r.domain}`} key={r.domain}>
            <div className="summary-domain">
              <span className={`tx-dot ${r.domain}`} />
              {domainLabel(r.domain)}
            </div>
            <div className={`summary-total bal-amount ${balanceTone(r.balance)}`}>
              {formatMoney(r.balance, currency)}
            </div>
            <div className="bal-breakdown">
              <span className="tx-amount neg">
                {formatMoney(r.charges, currency)}
              </span>
              <span className="bal-breakdown-label">en cargos</span>
              <span className="tx-amount pos">
                {formatMoney(r.credits, currency)}
              </span>
              <span className="bal-breakdown-label">en abonos</span>
            </div>
            <div className="summary-count">
              {formatNumber(r.count)}{" "}
              {r.count === 1 ? "movimiento" : "movimientos"}
            </div>
          </div>
        ))}
      </div>
    </section>
  );
}

/** Where the charges went — the question a customer asks about their balance. */
function ConceptosSection({
  data,
  currency,
}: {
  data: Statement;
  currency: LedgerCurrency;
}) {
  const rows = data.byType.filter((t) => t.currency === currency).slice(0, 12);
  if (rows.length === 0) return null;

  const largest = Math.abs(Number(rows[0]?.total ?? 0)) || 1;

  return (
    <section className="section">
      <SectionHead rule="servicios" title={`Cargos por concepto · ${currency}`} />
      <div className="card">
        <div className="concept-list">
          {rows.map((t) => (
            <div className="concept-row" key={`${t.name}-${t.currency}`}>
              <div className="concept-name">
                {txTypeLabel({ nameEn: t.name })}
                <span className="concept-count">
                  {formatNumber(t.count)}{" "}
                  {t.count === 1 ? "cargo" : "cargos"}
                </span>
              </div>
              <div className="concept-bar" aria-hidden>
                <span
                  style={{
                    width: `${Math.max(
                      2,
                      (Math.abs(Number(t.total)) / largest) * 100,
                    )}%`,
                  }}
                />
              </div>
              <div className="concept-total tx-amount neg">
                {formatMoney(t.total, currency)}
              </div>
            </div>
          ))}
        </div>
      </div>
    </section>
  );
}

/** The balance carried into the statement year — legacy's BALANCE FORWARD. */
function OpeningRow({
  opening,
  currency,
  year,
  canVoid,
}: {
  opening: string;
  currency: LedgerCurrency;
  year: number;
  canVoid: boolean;
}) {
  return (
    <tr>
      <td className="mono" style={{ whiteSpace: "nowrap" }}>
        {formatDate(`${year}-01-01T00:00:00.000Z`)}
      </td>
      <td className="tx-domain-cell">Ambas líneas</td>
      <td>
        Saldo anterior
        <div className="tx-concept">Al cierre de {year - 1}</div>
      </td>
      <td className="tx-ref">—</td>
      <td className="num">
        <span className={`tx-amount ${Number(opening) < 0 ? "neg" : "pos"}`}>
          {formatMoney(opening, currency)}
        </span>
      </td>
      <td className="num">
        <span className={`bal-running ${balanceTone(opening)}`}>
          {formatMoney(opening, currency)}
        </span>
      </td>
      {canVoid && <td />}
    </tr>
  );
}

function StatementRow({
  m,
  canVoid,
  onVoided,
}: {
  m: StatementMovement;
  canVoid: boolean;
  onVoided: () => void;
}) {
  const concept = m.message || m.period || null;
  const [busy, setBusy] = useState(false);

  async function doVoid() {
    if (
      !window.confirm(
        "¿Anular este movimiento? Quedará tachado y no contará en los totales.",
      )
    )
      return;
    setBusy(true);
    try {
      await voidMovement(m.id);
      onVoided();
    } catch (e) {
      window.alert(
        (e as Error)?.message ?? "No se pudo anular el movimiento.",
      );
      setBusy(false);
    }
  }

  return (
    <tr style={m.voided ? { textDecoration: "line-through", opacity: 0.55 } : undefined}>
      <td className="mono" style={{ whiteSpace: "nowrap" }}>
        {formatDate(m.transactionDate)}
      </td>
      <td className="tx-domain-cell">
        <span className={`tx-dot ${m.domain}`} />
        {domainLabel(m.domain)}
      </td>
      <td>
        {txTypeLabel(m.type)}
        {concept && <div className="tx-concept">{concept}</div>}
      </td>
      <td className="tx-ref">
        {m.reference || m.checkNumber || "—"}
        <div className="tx-concept">{ledgerSourceLabel(m.source)}</div>
      </td>
      <td className="num">
        <span className={`tx-amount ${m.direction === "charge" ? "neg" : "pos"}`}>
          {formatMoney(m.amount, m.currency)}
        </span>
        <div className="tx-cur">{directionLabel(m.direction)}</div>
      </td>
      <td className="num">
        <span className={`bal-running ${balanceTone(m.balanceAfter)}`}>
          {formatMoney(m.balanceAfter, m.currency)}
        </span>
      </td>
      {canVoid && (
        <td style={{ whiteSpace: "nowrap" }}>
          {!m.voided && (
            <button
              type="button"
              className="btn btn-ghost"
              style={{ padding: "4px 10px", fontSize: 12 }}
              onClick={doVoid}
              disabled={busy}
            >
              {busy ? "Anulando…" : "Anular"}
            </button>
          )}
        </td>
      )}
    </tr>
  );
}

function SectionHead({
  rule,
  title,
  count,
  countSuffix,
  right,
}: {
  rule: string;
  title: string;
  count?: number;
  countSuffix?: string;
  right?: React.ReactNode;
}) {
  return (
    <div
      className="section-head"
      style={
        right
          ? { display: "flex", alignItems: "center", gap: 12, flexWrap: "wrap" }
          : undefined
      }
    >
      <span className={`section-rule ${rule}`} aria-hidden />
      <h2 className="section-title">{title}</h2>
      {count != null && (
        <span className="section-count">
          {formatNumber(count)} {countSuffix ?? ""}
        </span>
      )}
      {right && (
        <div style={{ marginLeft: "auto" }}>{right}</div>
      )}
    </div>
  );
}

function StatementSkeleton() {
  return (
    <div aria-hidden>
      <div className="skeleton" style={{ height: 18, width: 180 }} />
      <div
        className="skeleton"
        style={{ height: 150, marginTop: 16, borderRadius: 16 }}
      />
      <div
        className="skeleton"
        style={{ height: 320, marginTop: 24, borderRadius: 16 }}
      />
    </div>
  );
}
