"use client";

import { use, useCallback, useEffect, useState } from "react";
import Link from "next/link";
import {
  AlertTriangle,
  ArrowLeft,
  Ban,
  ClipboardCheck,
  PackageCheck,
  Truck,
  Warehouse,
} from "lucide-react";
import { api, type Orden } from "@/lib/api";
import { usePuede } from "@/components/sesion-proveedor";
import { NombreProducto } from "@/components/nombre-producto";
import { ConsumosAdicionales } from "@/components/consumos-adicionales";
import { CostosOrden } from "@/components/costos-orden";
import { EjecucionOrden } from "@/components/ejecucion-orden";
import { AjustesOrden } from "@/components/ajustes-orden";
import { leerNombre } from "@/lib/producto";
import { EstadoBadge } from "@/components/estado-badge";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
import { Separator } from "@/components/ui/separator";
import { Skeleton } from "@/components/ui/skeleton";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from "@/components/ui/dialog";
import {
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from "@/components/ui/table";
import { toast } from "sonner";

const numero = new Intl.NumberFormat("es-GT", { maximumFractionDigits: 4 });

export default function PaginaOrden({ params }: { params: Promise<{ id: string }> }) {
  const { id } = use(params);
  const [orden, setOrden] = useState<Orden | null>(null);
  const [trabajando, setTrabajando] = useState(false);
  // Lo que el componente de consumos adicionales reporta hacia arriba: los pendientes
  // apagan «Registrar» (el servidor lo rechazaría igual), y los despachados se nombran
  // en el diálogo de producción, porque se van a gastar junto con la fórmula.
  const [consumos, setConsumos] = useState({ pendientes: 0, despachados: 0, conocido: true, fallo: false });

  const cargar = useCallback(async () => {
    try {
      setOrden(await api<Orden>(`/api/ordenes/${id}`));
    } catch (error) {
      toast.error(error instanceof Error ? error.message : "No se pudo cargar la orden.");
    }
  }, [id]);

  useEffect(() => {
    void cargar();
  }, [cargar]);

  async function ejecutar(ruta: string, cuerpo?: unknown, exito?: string) {
    setTrabajando(true);
    try {
      const actualizada = await api<Orden>(`/api/ordenes/${id}/${ruta}`, {
        metodo: "POST",
        cuerpo: cuerpo ?? {},
      });
      setOrden(actualizada);
      toast.success(exito ?? "Listo.");
    } catch (error) {
      // El backend redacta estos mensajes para que los lea quien está acá. Se muestran
      // enteros, sin recortar: dicen qué falta y qué se puede hacer en su lugar.
      toast.error(error instanceof Error ? error.message : "No se pudo completar la operación.", {
        duration: 10000,
      });
    } finally {
      setTrabajando(false);
    }
  }

  if (!orden) {
    return (
      <div className="space-y-3">
        <Skeleton className="h-9 w-48" />
        <Skeleton className="h-96 w-full" />
      </div>
    );
  }

  const faltantes = orden.insumos.filter((i) => i.faltante > 0);

  return (
    <div className="space-y-5">
      <div className="flex flex-wrap items-center gap-3">
        <Button variant="ghost" size="sm" asChild>
          <Link href="/">
            <ArrowLeft className="size-4" />
            Órdenes
          </Link>
        </Button>
        <h1 className="text-2xl font-semibold tracking-tight">Orden #{orden.id}</h1>
        <EstadoBadge estado={orden.estado} etiqueta={orden.estadoEtiqueta} />
        <span className="text-sm text-muted-foreground">
          {orden.linea} · {orden.fecha}
        </span>
      </div>

      <PasosDelFlujo
        orden={orden}
        trabajando={trabajando}
        ejecutar={ejecutar}
        consumos={consumos}
      />

      {faltantes.length > 0 && !orden.estado.startsWith("TERMIN") ? (
        <Alert>
          <AlertTriangle className="size-4" />
          <AlertTitle>
            {faltantes.length === 1
              ? "Un insumo no alcanzaba al planificar"
              : `${faltantes.length} insumos no alcanzaban al planificar`}
          </AlertTitle>
          <AlertDescription>
            El despacho no se bloquea por esto —el inventario admite existencias
            negativas y se ajusta por período—, pero conviene revisar bodega antes de
            despachar.
          </AlertDescription>
        </Alert>
      ) : null}

      <div className="grid gap-5 xl:grid-cols-2">
        <Card className="min-w-0">
          <CardHeader>
            <CardTitle className="text-base">Qué se produce</CardTitle>
            <CardDescription>
              Se planifica en unidades. Los lotes son sólo referencia de la receta.
            </CardDescription>
          </CardHeader>
          <CardContent>
            <Table>
              <TableHeader>
                <TableRow>
                  <TableHead>Producto</TableHead>
                  <TableHead className="text-right">Planificado</TableHead>
                  <TableHead className="hidden text-right sm:table-cell">Lotes</TableHead>
                  <TableHead className="text-right">Producido</TableHead>
                </TableRow>
              </TableHeader>
              <TableBody>
                {orden.detalles.map((d) => (
                  <TableRow key={d.plu}>
                    <TableCell>
                      <NombreProducto plu={d.plu} descripcion={d.descripcion} />
                    </TableCell>
                    <TableCell className="text-right tabular-nums">
                      {numero.format(d.cantidadPlanificada)}
                    </TableCell>
                    <TableCell className="hidden text-right tabular-nums text-muted-foreground sm:table-cell">
                      {d.lotesEquivalentes === null
                        ? "—"
                        : `${numero.format(d.lotesEquivalentes)} × ${numero.format(d.unidadesPorLote ?? 0)}`}
                    </TableCell>
                    <TableCell className="text-right tabular-nums">
                      {numero.format(d.cantidadProducida)}
                      {d.diferencia !== 0 && d.cantidadProducida > 0 ? (
                        <span
                          className={
                            d.diferencia < 0 ? "ml-2 text-amber-600" : "ml-2 text-emerald-600"
                          }
                        >
                          {d.diferencia > 0 ? "+" : ""}
                          {numero.format(d.diferencia)}
                        </span>
                      ) : null}
                    </TableCell>
                  </TableRow>
                ))}
              </TableBody>
            </Table>
          </CardContent>
        </Card>

        <Card className="min-w-0">
          <CardHeader>
            <CardTitle className="text-base">Materia prima</CardTitle>
            <CardDescription>
              Explotada hasta la hoja y congelada al planificar: baja por los
              sub-ensambles hasta la materia prima de verdad.
            </CardDescription>
          </CardHeader>
          <CardContent>
            {orden.insumos.length === 0 ? (
              <p className="py-8 text-center text-sm text-muted-foreground">
                Todavía no se planificó.
              </p>
            ) : (
              <Table>
                <TableHeader>
                  <TableRow>
                    <TableHead>Insumo</TableHead>
                    <TableHead className="text-right">Requerido</TableHead>
                    <TableHead className="text-right">Existencia</TableHead>
                  </TableRow>
                </TableHeader>
                <TableBody>
                  {orden.insumos.map((i) => (
                    <TableRow key={i.plu}>
                      <TableCell>
                        <NombreProducto plu={i.plu} descripcion={i.descripcion} />
                      </TableCell>
                      <TableCell className="text-right tabular-nums">
                        {numero.format(i.cantidadRequerida)}
                      </TableCell>
                      <TableCell className="text-right tabular-nums">
                        <span className={i.faltante > 0 ? "font-medium text-amber-600" : ""}>
                          {numero.format(i.existenciaAlPlanificar)}
                        </span>
                        {i.faltante > 0 ? (
                          <div className="text-xs text-amber-600">
                            faltan {numero.format(i.faltante)}
                          </div>
                        ) : null}
                      </TableCell>
                    </TableRow>
                  ))}
                </TableBody>
              </Table>
            )}
          </CardContent>
        </Card>
      </div>

      <ConsumosAdicionales
        orden={orden}
        onCambio={cargar}
        onResumenCambia={setConsumos}
      />

      <EjecucionOrden orden={orden} onCambio={cargar} />

      <CostosOrden orden={orden} onCambio={cargar} />

      <AjustesOrden orden={orden} />

      <Card className="min-w-0">
        <CardHeader>
          <CardTitle className="text-base">Dónde se movió el inventario</CardTitle>
        </CardHeader>
        <CardContent className="grid gap-4 text-sm sm:grid-cols-3">
          <Bodega
            titulo="Sale de"
            numero={orden.bodegaMateriaPrima}
            nombre={orden.bodegaMateriaPrimaNombre}
          />
          <Bodega
            titulo="Se produce en"
            numero={orden.bodegaProduccion}
            nombre={orden.bodegaProduccionNombre}
          />
          <Bodega
            titulo="Entra a"
            numero={orden.bodegaProductoTerminado}
            nombre={orden.bodegaProductoTerminadoNombre}
          />
          <Dato
            titulo="Despacho de materia prima"
            valor={orden.despachoDocumento ?? "—"}
            mono
          />
          <Dato
            titulo="Producción"
            valor={orden.produccionDocumento ?? "—"}
            mono
          />
          <Dato
            titulo="Traslado a producto terminado"
            valor={orden.trasladoDocumento ?? "—"}
            mono
          />
          {orden.documentosInventario.length > 0 ? (
            <div className="sm:col-span-3">
              <p className="text-xs text-muted-foreground">Todos los documentos de inventario de esta orden</p>
              <ul className="mt-1 space-y-0.5 font-mono text-sm">
                {orden.documentosInventario.map((d) => (
                  <li key={`${d.bodega}-${d.tipodoc}-${d.documento}`}>
                    {d.documento} · tipo {d.tipodoc} · bodega {d.bodega}
                    {d.origen != null && d.destino != null ? ` (${d.origen} → ${d.destino})` : ""}
                  </li>
                ))}
              </ul>
            </div>
          ) : null}
          <Dato titulo="Creada" valor={new Date(orden.creadoEn).toLocaleString("es-GT")} />
          {orden.observaciones ? (
            <div className="sm:col-span-3">
              <Separator className="mb-3" />
              <div className="text-xs uppercase tracking-wide text-muted-foreground">
                Observaciones
              </div>
              <p className="whitespace-pre-line">{orden.observaciones}</p>
            </div>
          ) : null}
        </CardContent>
      </Card>
    </div>
  );
}

/**
 * Una bodega, como se lee: el nombre que le puso el cliente y el número chiquito al lado.
 *
 * ⚠️ El número NO se esconde del todo aunque nadie lo lea a diario: es lo que queda en
 * `maedoc` y en `detmov`, así que cuando alguien vaya a cuadrar un movimiento contra el
 * Back Office lo va a necesitar. Lo que cambia es cuál de los dos manda a la vista.
 */
function Bodega({
  titulo,
  numero,
  nombre,
}: {
  titulo: string;
  numero: number;
  nombre: string | null;
}) {
  return (
    <div>
      <div className="text-xs uppercase tracking-wide text-muted-foreground">{titulo}</div>
      <div className="font-medium">{nombre ?? `Bodega ${numero}`}</div>
      <div className="text-xs text-muted-foreground">#{numero}</div>
    </div>
  );
}

function Dato({ titulo, valor, mono }: { titulo: string; valor: string; mono?: boolean }) {
  return (
    <div>
      <div className="text-xs uppercase tracking-wide text-muted-foreground">{titulo}</div>
      <div className={mono ? "font-mono" : ""}>{valor}</div>
    </div>
  );
}

/**
 * Los cuatro pasos del pizarrón, en orden y con el botón que corresponde.
 *
 * Sólo se ofrece la transición que el servidor declara posible (`transicionesPosibles`),
 * así que la pantalla no puede quedar desalineada con las reglas: si el dominio cambia,
 * cambia lo que se ve, sin tocar esto.
 */
function PasosDelFlujo({
  orden,
  trabajando,
  ejecutar,
  consumos,
}: {
  orden: Orden;
  trabajando: boolean;
  ejecutar: (ruta: string, cuerpo?: unknown, exito?: string) => Promise<void>;
  consumos: { pendientes: number; despachados: number; conocido: boolean; fallo: boolean };
}) {
  const puede = (v: string) => orden.transicionesPosibles.some((t) => t.valor === v);
  const puedeEditar = usePuede("editar");
  const puedeDespachar = usePuede("despachar_mp");
  const puedeRegistrar = usePuede("registrar_produccion");
  const puedeTrasladar = usePuede("trasladar_pt");
  const puedeAnular = usePuede("anular");

  return (
    <Card className="min-w-0">
      <CardContent className="flex flex-wrap items-center gap-x-3 gap-y-2 py-4">
        <Paso numero={1} titulo="Planificar" hecho={orden.insumos.length > 0} />
        {puede("PLANIFICADA") && puedeEditar ? (
          <Button
            size="sm"
            disabled={trabajando}
            onClick={() => void ejecutar("planificar", {}, "Materia prima calculada y congelada.")}
          >
            <ClipboardCheck className="size-4" />
            Planificar
          </Button>
        ) : null}

        <Flecha />
        <Paso numero={2} titulo="Despachar materia prima" hecho={!!orden.despachoDocumento} />
        {puede("MP_DESPACHADA") && puedeDespachar ? (
          <Button
            size="sm"
            disabled={trabajando}
            onClick={() =>
              void ejecutar("despachar-materia-prima", {}, "Materia prima despachada a producción.")
            }
          >
            <Warehouse className="size-4" />
            Despachar
          </Button>
        ) : null}

        <Flecha />
        <Paso
          numero={3}
          titulo="Registrar producción"
          hecho={!!orden.produccionDocumento}
        />
        {puede("PRODUCIDA") && puedeRegistrar && (!orden.entradaImplicaTraslado || puedeTrasladar) ? (
          consumos.pendientes > 0 || !consumos.conocido ? (
            // El servidor igual lo rechaza —esa materia prima saldría de una bodega
            // donde nunca entró—, pero llegar al error habiendo llenado el diálogo es
            // peor que no poder apretarlo.
            <div className="flex flex-col gap-0.5">
              <Button size="sm" disabled>
                <ClipboardCheck className="size-4" />
                Registrar
              </Button>
              <span className="text-xs text-amber-600">
                {!consumos.conocido
                  ? consumos.fallo
                    ? "No se pudo verificar la materia prima adicional: recargue"
                    : "Verificando la materia prima adicional…"
                  : consumos.pendientes === 1
                    ? "Hay una materia prima adicional sin despachar"
                    : `Hay ${consumos.pendientes} materias primas adicionales sin despachar`}
              </span>
            </div>
          ) : (
            <DialogoProduccion
              // Al capturar la producción real la orden se refresca pero el diálogo conserva su estado: sin esta llave seguiría
              // ofreciendo lo planificado y sobrescribiría lo capturado.
              key={`${orden.estado}|${orden.detalles.map((d) => d.cantidadProducida).join(",")}`}
              orden={orden}
              trabajando={trabajando}
              ejecutar={ejecutar}
              consumosDespachados={consumos.despachados}
            />
          )
        ) : null}

        <Flecha />
        <Paso numero={4} titulo="Trasladar a producto terminado" hecho={!!orden.trasladoDocumento} />
        {puede("TERMINADA") && (orden.trasladoPendienteReal ? puedeTrasladar : puedeRegistrar) ? (
          <Button
            size="sm"
            disabled={trabajando}
            onClick={() =>
              void ejecutar(
                "trasladar-producto-terminado",
                {},
                "Producto terminado trasladado a su bodega.",
              )
            }
          >
            <Truck className="size-4" />
            Trasladar
          </Button>
        ) : null}

        {puede("ANULADA") && puedeAnular ? (
          <div className="ml-auto">
            <DialogoAnular trabajando={trabajando} ejecutar={ejecutar} />
          </div>
        ) : null}
      </CardContent>
    </Card>
  );
}

function Paso({ numero, titulo, hecho }: { numero: number; titulo: string; hecho: boolean }) {
  return (
    <div className="flex items-center gap-2">
      <span
        className={
          hecho
            ? "flex size-6 items-center justify-center rounded-full bg-emerald-600 text-xs font-medium text-white"
            : "flex size-6 items-center justify-center rounded-full border text-xs font-medium text-muted-foreground"
        }
      >
        {hecho ? <PackageCheck className="size-3.5" /> : numero}
      </span>
      <span className={hecho ? "text-sm" : "text-sm text-muted-foreground"}>{titulo}</span>
    </div>
  );
}

function Flecha() {
  return <span className="text-muted-foreground">→</span>;
}

function DialogoProduccion({
  orden,
  trabajando,
  ejecutar,
  consumosDespachados,
}: {
  orden: Orden;
  trabajando: boolean;
  ejecutar: (ruta: string, cuerpo?: unknown, exito?: string) => Promise<void>;
  /** Renglones de materia prima adicional ya en producción, que también se van a gastar. */
  consumosDespachados: number;
}) {
  const [abierto, setAbierto] = useState(false);
  const [cantidades, setCantidades] = useState<Record<string, string>>(() =>
    Object.fromEntries(orden.detalles.map((d) => [d.plu, String(orden.estado === "EN_EJECUCION" ? d.cantidadProducida : d.cantidadPlanificada)])),
  );

  return (
    <Dialog open={abierto} onOpenChange={setAbierto}>
      <DialogTrigger asChild>
        <Button size="sm" disabled={trabajando}>
          <ClipboardCheck className="size-4" />
          Registrar
        </Button>
      </DialogTrigger>
      <DialogContent>
        <DialogHeader>
          <DialogTitle>¿Cuánto salió de verdad?</DialogTitle>
          <DialogDescription>
            Viene precargado con lo planificado. Corregilo con lo que realmente se
            produjo: con este número entra el producto terminado a la bodega de
            producción y sale de ahí la materia prima que se gastó. Lo que sobre se
            queda en la bodega, igual que en la vida real.
            {consumosDespachados > 0 ? (
              <>
                {" "}
                Se gastará además la materia prima adicional listada arriba (
                {consumosDespachados}{" "}
                {consumosDespachados === 1 ? "renglón" : "renglones"}).
              </>
            ) : null}
          </DialogDescription>
        </DialogHeader>
        <div className="space-y-3">
          {orden.detalles.map((d) => (
            <div key={d.plu} className="space-y-1">
              <Label htmlFor={`prod-${d.plu}`}>
                {leerNombre(d.descripcion ?? d.plu).nombre}
              </Label>
              <Input
                id={`prod-${d.plu}`}
                type="number"
                min={0}
                step="any"
                value={cantidades[d.plu] ?? ""}
                onChange={(e) =>
                  setCantidades((prev) => ({ ...prev, [d.plu]: e.target.value }))
                }
              />
              <p className="text-xs text-muted-foreground">
                Planificado: {numero.format(d.cantidadPlanificada)}
              </p>
            </div>
          ))}
        </div>
        <DialogFooter>
          <Button variant="outline" onClick={() => setAbierto(false)}>
            Cancelar
          </Button>
          <Button
            disabled={trabajando}
            onClick={async () => {
              await ejecutar(
                "registrar-produccion",
                {
                  producidas: orden.detalles.map((d) => ({
                    plu: d.plu,
                    cantidad: Number(cantidades[d.plu] ?? 0),
                  })),
                },
                "Producción registrada.",
              );
              setAbierto(false);
            }}
          >
            Guardar
          </Button>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}

function DialogoAnular({
  trabajando,
  ejecutar,
}: {
  trabajando: boolean;
  ejecutar: (ruta: string, cuerpo?: unknown, exito?: string) => Promise<void>;
}) {
  const [abierto, setAbierto] = useState(false);
  const [motivo, setMotivo] = useState("");

  return (
    <Dialog open={abierto} onOpenChange={setAbierto}>
      <DialogTrigger asChild>
        <Button size="sm" variant="outline" disabled={trabajando}>
          <Ban className="size-4" />
          Anular
        </Button>
      </DialogTrigger>
      <DialogContent>
        <DialogHeader>
          <DialogTitle>Anular la orden</DialogTitle>
          <DialogDescription>
            Anular <strong>no revierte</strong> el inventario que ya se movió. Si la
            orden despachó materia prima, esa materia prima está físicamente en
            producción y su documento existe en el Back Office; revertirlo se hace allá,
            sobre el documento.
          </DialogDescription>
        </DialogHeader>
        <div className="space-y-2">
          <Label htmlFor="motivo">¿Por qué se anula?</Label>
          <Textarea
            id="motivo"
            value={motivo}
            onChange={(e) => setMotivo(e.target.value)}
            placeholder="Queda guardado en la orden y en la bitácora."
          />
        </div>
        <DialogFooter>
          <Button variant="outline" onClick={() => setAbierto(false)}>
            Cancelar
          </Button>
          <Button
            variant="destructive"
            disabled={trabajando || motivo.trim() === ""}
            onClick={async () => {
              await ejecutar("anular", { motivo }, "Orden anulada.");
              setAbierto(false);
            }}
          >
            Anular
          </Button>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}
