"use client";

import { useCallback, useEffect, useMemo, useState } from "react";
import type { ReactNode } from "react";
import Link from "next/link";
import {
  AppWindow,
  Archive,
  ArrowDown,
  ArrowUp,
  Check,
  ChevronDown,
  ChevronRight,
  ChevronsUp,
  Clipboard,
  Copy,
  Image as LucideImage,
  Layers,
  ListFilter,
  Loader2,
  Pencil,
  Printer,
  RefreshCw,
  Scissors,
  Search,
  Trash2,
  X,
} from "lucide-react";
import Button from "@/components/ui/Button";
import CortesEditor from "@/components/cortes/CortesEditor";
import LegacyAppFrame from "@/components/layout/LegacyAppFrame";
import { cn } from "@/lib/cn";
import { getPB, fileUrl } from "@/lib/pocketbase";
import { normalizeFotos } from "@/components/pedidos/PhotoCapture";
import { corteItemToDraft } from "@/lib/cortes/drafts";
import { formatJobCuts } from "@/lib/cortes/copy";
import { matHasCanto, materialLabel, getMatGroup, getMaterialColorStyle } from "@/lib/cortes/materials";
import { groupItemsByMaterial, packCuts, type PackedBoard } from "@/lib/cortes/optimizer";
import { isCutItemComplete, nextCantoProgress, summarizeJobProgress } from "@/lib/cortes/progress";
import type { CorteItem, CorteJobWithItems } from "@/lib/cortes/types";
import type { CorteItemDraft } from "@/lib/cortes/types";
import {
  archiveCorteJob,
  deleteCorteItem,
  listArchivedCorteJobsWithItems,
  listCorteJobsWithItems,
  reorderCorteItems,
  reorderCorteJobs,
  sortCorteItemsByMaterial,
  updateCorteItem,
  createCorteItem,
} from "@/lib/pb/cortes";
import { useAuthStore, isAdmin } from "@/lib/store/useAuthStore";

type ViewMode = "trabajos" | "materiales" | "optimizador";

const BOARD_COLORS = [
  "#8A8E75",
  "#68604D",
  "#BEC5A4",
  "#A69578",
  "#D5C7AD",
  "#2D2F22",
];

const COLLAPSED_KEY = "fibro_cortes_collapsed_jobs_v1";

export default function CortesPage() {
  const user = useAuthStore((s) => s.user);
  const canAdmin = isAdmin(user);
  const [jobs, setJobs] = useState<CorteJobWithItems[]>([]);
  const [archivedJobs, setArchivedJobs] = useState<CorteJobWithItems[]>([]);
  const [loading, setLoading] = useState(true);
  const [view, setView] = useState<ViewMode>("trabajos");
  const [showLegacy, setShowLegacy] = useState(false);
  const [busyId, setBusyId] = useState<string | null>(null);
  const [copiedId, setCopiedId] = useState<string | null>(null);
  const [collapsed, setCollapsed] = useState<Set<string>>(new Set());
  const [search, setSearch] = useState("");
  const [editing, setEditing] = useState<{ jobId: string; item?: CorteItem; draft: CorteItemDraft } | null>(null);
  const [err, setErr] = useState("");

  const load = useCallback(async () => {
    setLoading(true);
    setErr("");
    try {
      const [active, archived] = await Promise.all([
        listCorteJobsWithItems(),
        listArchivedCorteJobsWithItems(),
      ]);
      setJobs(active);
      setArchivedJobs(archived);
    } catch (e) {
      setErr((e as Error).message || "No se pudo cargar Cortes");
    } finally {
      setLoading(false);
    }
  }, []);

  useEffect(() => {
    load();
  }, [load]);

  // Realtime: si Edd o Anibal modifican cortes, la pantalla del otro se actualiza
  // sola (sin refrescar el navegador). Debounce para coalescer rafagas de cambios.
  useEffect(() => {
    const pb = getPB();
    let unsubJobs: (() => void) | undefined;
    let unsubItems: (() => void) | undefined;
    let timer: ReturnType<typeof setTimeout> | undefined;
    const scheduleReload = () => {
      if (timer) clearTimeout(timer);
      timer = setTimeout(() => { load(); }, 400);
    };
    (async () => {
      try {
        unsubJobs = await pb.collection("corte_jobs").subscribe("*", scheduleReload);
        unsubItems = await pb.collection("corte_items").subscribe("*", scheduleReload);
      } catch {
        /* realtime no disponible; queda el refresh manual */
      }
    })();
    return () => {
      if (timer) clearTimeout(timer);
      unsubJobs?.();
      unsubItems?.();
    };
  }, [load]);

  useEffect(() => {
    try {
      const raw = localStorage.getItem(COLLAPSED_KEY);
      if (raw) setCollapsed(new Set(JSON.parse(raw)));
    } catch {
      /* noop */
    }
  }, []);

  const allItems = useMemo(
    () => jobs.flatMap((job) => job.items.map((item) => ({ ...item, expand: { job } }))),
    [jobs],
  );
  const progress = useMemo(() => summarizeJobProgress(allItems), [allItems]);
  const visibleJobs = useMemo(() => {
    const q = search.trim().toLowerCase();
    if (!q) return jobs;
    return jobs.filter((job) => {
      const haystack = [
        job.numero || "",
        job.cliente_nombre || "",
        job.source || "",
        ...job.items.flatMap((item) => [item.material, item.color || "", item.nota || ""]),
      ].join(" ").toLowerCase();
      return haystack.includes(q);
    });
  }, [jobs, search]);
  const visibleArchivedJobs = useMemo(() => {
    const q = search.trim().toLowerCase();
    const archived = archivedJobs.slice(0, 30);
    if (!q) return archived;
    return archived.filter((job) => {
      const haystack = [
        job.numero || "",
        job.cliente_nombre || "",
        job.source || "",
        ...job.items.flatMap((item) => [item.material, item.color || "", item.nota || ""]),
      ].join(" ").toLowerCase();
      return haystack.includes(q);
    });
  }, [archivedJobs, search]);

  function replaceLocalItem(updated: CorteItem) {
    setJobs((prev) =>
      prev.map((job) =>
        job.id === updated.job
          ? { ...job, items: job.items.map((item) => (item.id === updated.id ? updated : item)) }
          : job,
      ),
    );
  }

  async function toggleEstado(item: CorteItem) {
    setBusyId(item.id);
    try {
      const next = item.estado === "cortado" ? "pendiente" : "cortado";
      replaceLocalItem(await updateCorteItem(item, { estado: next }));
    } finally {
      setBusyId(null);
    }
  }

  async function cycleCanto(item: CorteItem, dim: "ancho" | "alto") {
    setBusyId(`${item.id}-${dim}`);
    try {
      const patch =
        dim === "ancho"
          ? { canto_ancho_hecho: nextCantoProgress(item.canto_ancho_hecho, item.canto_ancho) }
          : { canto_alto_hecho: nextCantoProgress(item.canto_alto_hecho, item.canto_alto) };
      replaceLocalItem(await updateCorteItem(item, patch));
    } finally {
      setBusyId(null);
    }
  }

  async function copyJob(job: CorteJobWithItems) {
    await navigator.clipboard.writeText(formatJobCuts(job));
    setCopiedId(job.id);
    setTimeout(() => setCopiedId(null), 1800);
  }

  async function archiveJob(job: CorteJobWithItems) {
    if (!confirm(`¿Archivar el trabajo ${job.numero || "sin numero"}?`)) return;
    setBusyId(job.id);
    try {
      await archiveCorteJob(job);
      setJobs((prev) => prev.filter((j) => j.id !== job.id));
    } finally {
      setBusyId(null);
    }
  }

  function toggleCollapsed(jobId: string) {
    setCollapsed((prev) => {
      const next = new Set(prev);
      next.has(jobId) ? next.delete(jobId) : next.add(jobId);
      try {
        localStorage.setItem(COLLAPSED_KEY, JSON.stringify([...next]));
      } catch {
        /* noop */
      }
      return next;
    });
  }

  async function moveJob(jobId: string, direction: "up" | "down" | "top") {
    const index = jobs.findIndex((job) => job.id === jobId);
    if (index < 0) return;
    const next = [...jobs];
    const [job] = next.splice(index, 1);
    const target = direction === "top" ? 0 : direction === "up" ? Math.max(0, index - 1) : Math.min(next.length, index + 1);
    next.splice(target, 0, job);
    const reindexed = next.map((item, i) => ({ ...item, sort_order: i }));
    setJobs(reindexed);
    setBusyId(`job-order-${jobId}`);
    try {
      await reorderCorteJobs(reindexed);
    } catch (e) {
      setErr((e as Error).message || "No se pudo ordenar el trabajo");
      load();
    } finally {
      setBusyId(null);
    }
  }

  async function sortJobByMaterial(job: CorteJobWithItems) {
    setBusyId(`job-sort-${job.id}`);
    try {
      const items = await sortCorteItemsByMaterial(job);
      setJobs((prev) => prev.map((j) => (j.id === job.id ? { ...j, items } : j)));
    } catch (e) {
      setErr((e as Error).message || "No se pudo ordenar por material");
    } finally {
      setBusyId(null);
    }
  }

  async function deleteItem(item: CorteItem) {
    if (!confirm("¿Borrar este corte?")) return;
    setBusyId(`item-delete-${item.id}`);
    try {
      await deleteCorteItem(item);
      setJobs((prev) =>
        prev.map((job) =>
          job.id === item.job
            ? { ...job, items: job.items.filter((candidate) => candidate.id !== item.id) }
            : job,
        ),
      );
    } catch (e) {
      setErr((e as Error).message || "No se pudo borrar el corte");
    } finally {
      setBusyId(null);
    }
  }

  async function moveItem(job: CorteJobWithItems, itemId: string, direction: "up" | "down") {
    const index = job.items.findIndex((item) => item.id === itemId);
    if (index < 0) return;
    const target = direction === "up" ? index - 1 : index + 1;
    if (target < 0 || target >= job.items.length) return;
    const nextItems = [...job.items];
    [nextItems[index], nextItems[target]] = [nextItems[target], nextItems[index]];
    setJobs((prev) => prev.map((candidate) => (candidate.id === job.id ? { ...candidate, items: nextItems } : candidate)));
    setBusyId(`item-order-${itemId}`);
    try {
      const updated = await reorderCorteItems(job.id, nextItems);
      setJobs((prev) => prev.map((candidate) => (candidate.id === job.id ? { ...candidate, items: updated } : candidate)));
    } catch (e) {
      setErr((e as Error).message || "No se pudo mover el corte");
      load();
    } finally {
      setBusyId(null);
    }
  }

  async function saveEditedItem() {
    if (!editing) return;
    const key = editing.item ? `item-edit-${editing.item.id}` : `item-create-${editing.jobId}`;
    setBusyId(key);
    try {
      const patch = { ...editing.draft } as Partial<CorteItem>;
      delete patch.id;
      delete patch.legacy_item_id;
      delete patch.sort_order;
      
      if (editing.item) {
        const updated = await updateCorteItem(editing.item, patch);
        replaceLocalItem(updated);
      } else {
        const created = await createCorteItem(editing.jobId, editing.draft);
        setJobs((prev) =>
          prev.map((job) =>
            job.id === editing.jobId
              ? { ...job, items: [...job.items, created] }
              : job,
          ),
        );
      }
      setEditing(null);
    } catch (e) {
      setErr((e as Error).message || "No se pudo guardar el corte");
    } finally {
      setBusyId(null);
    }
  }

  const handleAddCorteItem = (jobId: string) => {
    const blankDraft: CorteItemDraft = {
      cantidad: 1,
      ancho: 0,
      alto: 0,
      material: "18b",
      color: "",
      canto_ancho: 0,
      canto_alto: 0,
      veta: "ninguna",
      nota: "",
      estado: "pendiente",
    };
    setEditing({ jobId, draft: blankDraft });
  };

  const hasJobs = jobs.length > 0;

  return (
    <div className="max-w-7xl space-y-3">
      <div className="no-print flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between">
        <div>
          <div className="flex items-center gap-3">
            <Scissors className="h-6 w-6 text-brand-dark" />
            <h1 className="text-xl font-extrabold text-ink">Cortes</h1>
          </div>
          <p className="mt-0.5 text-xs font-medium text-ink-soft">
            {progress.complete}/{progress.total} cortes completos · {jobs.length} trabajo{jobs.length !== 1 ? "s" : ""}
          </p>
        </div>

        <div className="flex flex-wrap items-center gap-2">
          <label className="relative">
            <Search size={14} className="pointer-events-none absolute left-2.5 top-1/2 -translate-y-1/2 text-faint" />
            <input
              value={search}
              onChange={(e) => setSearch(e.target.value)}
              placeholder="Buscar trabajo"
              className="h-9 w-52 rounded-lg border border-line bg-surface pl-8 pr-3 text-sm text-ink outline-none focus:border-brand"
            />
          </label>
          <ViewSwitch view={view} setView={setView} />
          <button
            onClick={load}
            className="inline-flex h-9 w-9 items-center justify-center rounded-lg border border-line bg-surface text-faint hover:text-ink"
            title="Actualizar"
          >
            <RefreshCw size={16} className={loading ? "animate-spin" : ""} />
          </button>
          <button
            onClick={() => window.print()}
            className="inline-flex h-9 w-9 items-center justify-center rounded-lg border border-line bg-surface text-faint hover:text-ink"
            title="Imprimir"
          >
            <Printer size={16} />
          </button>
        </div>
      </div>

      {err && (
        <div className="rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-bad">
          {err}
        </div>
      )}

      {loading ? (
        <div className="grid place-items-center py-20 text-faint">
          <Loader2 className="h-6 w-6 animate-spin" />
        </div>
      ) : !hasJobs ? (
        <EmptyState />
      ) : view === "trabajos" ? (
        (() => {
          const pendingJobs = visibleJobs.filter(job => !summarizeJobProgress(job.items).isComplete);
          const completedJobs = visibleJobs.filter(job => summarizeJobProgress(job.items).isComplete);
          return (
            <div className="space-y-6">
              {pendingJobs.length > 0 && (
                <div className="space-y-2">
                  <h2 className="text-sm font-extrabold text-ink-soft flex items-center gap-2">
                    <span className="h-2 w-2 rounded-full bg-amber-500" />
                    CORTES PENDIENTES ({pendingJobs.length})
                  </h2>
                  <div className="space-y-2">
                    {pendingJobs.map((job, index) => (
                      <JobCard
                        key={job.id}
                        job={job}
                        busyId={busyId}
                        copied={copiedId === job.id}
                        canAdmin={canAdmin}
                        collapsed={collapsed.has(job.id)}
                        canMoveUp={index > 0}
                        canMoveDown={index < pendingJobs.length - 1}
                        onToggle={toggleEstado}
                        onCycleCanto={cycleCanto}
                        onCopy={copyJob}
                        onArchive={archiveJob}
                        onToggleCollapsed={toggleCollapsed}
                        onMove={moveJob}
                        onSortByMaterial={sortJobByMaterial}
                        onAddItem={handleAddCorteItem}
                        onEditItem={(item) => setEditing({ jobId: job.id, item, draft: corteItemToDraft(item) })}
                        onDeleteItem={deleteItem}
                        onMoveItem={moveItem}
                      />
                    ))}
                  </div>
                </div>
              )}
              {completedJobs.length > 0 && (
                <div className="space-y-2">
                  <h2 className="text-sm font-extrabold text-ink-soft flex items-center gap-2">
                    <span className="h-2 w-2 rounded-full bg-brand" />
                    TODOS LOS CORTES LISTOS ({completedJobs.length})
                  </h2>
                  <div className="space-y-2">
                    {completedJobs.map((job, index) => (
                      <JobCard
                        key={job.id}
                        job={job}
                        busyId={busyId}
                        copied={copiedId === job.id}
                        canAdmin={canAdmin}
                        collapsed={collapsed.has(job.id)}
                        canMoveUp={index > 0}
                        canMoveDown={index < completedJobs.length - 1}
                        onToggle={toggleEstado}
                        onCycleCanto={cycleCanto}
                        onCopy={copyJob}
                        onArchive={archiveJob}
                        onToggleCollapsed={toggleCollapsed}
                        onMove={moveJob}
                        onSortByMaterial={sortJobByMaterial}
                        onAddItem={handleAddCorteItem}
                        onEditItem={(item) => setEditing({ jobId: job.id, item, draft: corteItemToDraft(item) })}
                        onDeleteItem={deleteItem}
                        onMoveItem={moveItem}
                      />
                    ))}
                  </div>
                </div>
              )}
            </div>
          );
        })()
      ) : view === "materiales" ? (
        <MaterialView jobs={jobs} busyId={busyId} onToggle={toggleEstado} onCycleCanto={cycleCanto} />
      ) : (
        <OptimizerView jobs={jobs} />
      )}

      {!loading && visibleArchivedJobs.length > 0 && (
        <section className="no-print mt-5 rounded-xl border border-line bg-surface/70 p-3">
          <div className="mb-3 flex items-center justify-between gap-3">
            <div>
              <h2 className="flex items-center gap-2 text-sm font-extrabold text-ink">
                <Archive size={16} /> Archivados
              </h2>
              <p className="text-xs text-faint">
                Ultimos {visibleArchivedJobs.length} trabajos cerrados o entregados.
              </p>
            </div>
          </div>
          <div className="grid gap-2 md:grid-cols-2">
            {visibleArchivedJobs.map((job) => (
              <ArchivedJobCard key={job.id} job={job} />
            ))}
          </div>
        </section>
      )}

      {editing && (
        <div className="fixed inset-0 z-40 grid place-items-center bg-black/35 p-4" onClick={() => setEditing(null)}>
          <div className="w-full max-w-5xl rounded-xl border border-line bg-surface p-4 shadow-[var(--shadow-lg)]" onClick={(e) => e.stopPropagation()}>
            <div className="mb-3 flex items-center justify-between gap-3">
              <h2 className="font-extrabold text-ink">Editar corte</h2>
              <button type="button" className="rounded-lg px-2 py-1 text-faint hover:text-ink" onClick={() => setEditing(null)}>
                Cerrar
              </button>
            </div>
            <CortesEditor
              items={[editing.draft]}
              allowAdd={false}
              allowRemove={false}
              onChange={(items) => setEditing((current) => current ? { ...current, draft: items[0] || current.draft } : current)}
            />
            <div className="mt-4 flex justify-end gap-2">
              <Button type="button" variant="secondary" onClick={() => setEditing(null)}>Cancelar</Button>
              <Button
                type="button"
                onClick={saveEditedItem}
                loading={busyId === (editing.item ? `item-edit-${editing.item.id}` : `item-create-${editing.jobId}`)}
              >
                Guardar corte
              </Button>
            </div>
          </div>
        </div>
      )}

      {canAdmin && (
        <div className="no-print border-t border-line pt-4">
          <button
            type="button"
            onClick={() => setShowLegacy((v) => !v)}
            className="inline-flex items-center gap-2 rounded-xl border border-line bg-surface px-3 py-2 text-sm font-medium text-faint hover:text-ink"
          >
            <AppWindow size={16} /> Respaldo legacy
          </button>
          {showLegacy && (
            <div className="mt-4">
              <LegacyAppFrame
                title="Cortes legacy"
                description="Respaldo temporal durante el piloto. No cargar trabajos nuevos acá salvo emergencia."
                src="/legacy/cortes/index.html"
              />
            </div>
          )}
        </div>
      )}
    </div>
  );
}

function ViewSwitch({ view, setView }: { view: ViewMode; setView: (view: ViewMode) => void }) {
  const options: { value: ViewMode; label: string; icon: typeof Scissors }[] = [
    { value: "trabajos", label: "Trabajos", icon: Scissors },
    { value: "materiales", label: "Material", icon: Layers },
    { value: "optimizador", label: "Optimizador", icon: Clipboard },
  ];
  return (
    <div className="inline-flex rounded-lg border border-line bg-surface p-0.5">
      {options.map((option) => {
        const Icon = option.icon;
        return (
          <button
            key={option.value}
            type="button"
            aria-label={option.label}
            aria-pressed={view === option.value}
            title={option.label}
            onClick={() => setView(option.value)}
            className={cn(
              "inline-flex h-8 items-center gap-1.5 rounded-md px-2.5 text-xs font-semibold",
              view === option.value ? "bg-brand-soft text-brand-dark" : "text-faint hover:text-ink",
            )}
          >
            <Icon size={15} />
            <span className="hidden sm:inline">{option.label}</span>
          </button>
        );
      })}
    </div>
  );
}

function JobCard({
  job,
  busyId,
  copied,
  canAdmin,
  collapsed,
  canMoveUp,
  canMoveDown,
  onToggle,
  onCycleCanto,
  onCopy,
  onArchive,
  onToggleCollapsed,
  onMove,
  onSortByMaterial,
  onAddItem,
  onEditItem,
  onDeleteItem,
  onMoveItem,
}: {
  job: CorteJobWithItems;
  busyId: string | null;
  copied: boolean;
  canAdmin: boolean;
  collapsed: boolean;
  canMoveUp: boolean;
  canMoveDown: boolean;
  onToggle: (item: CorteItem) => void;
  onCycleCanto: (item: CorteItem, dim: "ancho" | "alto") => void;
  onCopy: (job: CorteJobWithItems) => void;
  onArchive: (job: CorteJobWithItems) => void;
  onToggleCollapsed: (jobId: string) => void;
  onMove: (jobId: string, direction: "up" | "down" | "top") => void;
  onSortByMaterial: (job: CorteJobWithItems) => void;
  onAddItem: (jobId: string) => void;
  onEditItem: (item: CorteItem) => void;
  onDeleteItem: (item: CorteItem) => void;
  onMoveItem: (job: CorteJobWithItems, itemId: string, direction: "up" | "down") => void;
}) {
  const progress = summarizeJobProgress(job.items);
  const linkedPedido = job.pedido;
  // Cortes incompletos arriba; al completarse vuelven a su orden de la lista (sort_order).
  const orderedItems = [...job.items].sort((a, b) => {
    const ca = isCutItemComplete(a) ? 1 : 0;
    const cb = isCutItemComplete(b) ? 1 : 0;
    if (ca !== cb) return ca - cb;
    return (a.sort_order || 0) - (b.sort_order || 0);
  });
  const [imagePopup, setImagePopup] = useState(false);
  const [imageTooltip, setImageTooltip] = useState<{ x: number; y: number } | null>(null);
  const pedido = job.expand?.pedido;
  const pedidoFotos = normalizeFotos(pedido?.dibujo_png);
  const firstFoto = pedidoFotos[0];
  const hasImage = !!firstFoto && !!pedido;
  return (
    <section className="overflow-hidden rounded-lg border border-line bg-surface">
      <header className="flex flex-col gap-2 border-b border-line px-3 py-2 md:flex-row md:items-center md:justify-between">
        <div>
          <div className="flex flex-wrap items-center gap-1.5">
            <button
              type="button"
              onClick={() => onToggleCollapsed(job.id)}
              className="inline-flex items-center gap-1 text-base font-extrabold leading-tight text-ink hover:text-brand-dark"
              title={collapsed ? "Desplegar trabajo" : "Plegar trabajo"}
            >
              {collapsed ? <ChevronRight size={16} /> : <ChevronDown size={16} />}
              {job.numero ? `#${job.numero}` : "Sin numero"}
            </button>
            <span
              className={cn(
                "rounded-full px-1.5 py-0.5 text-[11px] font-bold leading-none",
                progress.isComplete ? "bg-brand-soft text-brand-dark" : "bg-amber-50 text-amber-800",
              )}
            >
              {progress.complete}/{progress.total}
            </span>
            {job.expand?.pedido?.urgente && (
              <span className="rounded-full bg-red-500 px-1.5 py-0.5 text-[11px] font-extrabold leading-none text-white tracking-wide">
                URGENTE
              </span>
            )}
            {hasImage && (
              <button
                type="button"
                title="Ver imagen del pedido"
                className="inline-flex items-center rounded-full border border-line bg-canvas px-1.5 py-0.5 text-faint hover:border-line-strong hover:text-ink"
                onMouseEnter={(e) => setImageTooltip({ x: e.clientX, y: e.clientY })}
                onMouseMove={(e) => setImageTooltip({ x: e.clientX, y: e.clientY })}
                onMouseLeave={() => setImageTooltip(null)}
                onClick={() => { setImageTooltip(null); setImagePopup(true); }}
              >
                <LucideImage size={11} />
              </button>
            )}
            {job.source === "legacy" && (
              <span className="rounded-full bg-canvas px-1.5 py-0.5 text-[11px] font-bold leading-none text-faint">
                legacy
              </span>
            )}
          </div>
          <p className="mt-0.5 text-xs text-ink-soft">
            {job.cliente_nombre || "Sin cliente"} {linkedPedido ? "· vinculado a Pedido" : ""}
          </p>
        </div>

        <div className="flex flex-wrap gap-1.5">
          <IconButton
            label="Subir arriba"
            disabled={!canMoveUp || busyId === `job-order-${job.id}`}
            onClick={() => onMove(job.id, "top")}
          >
            <ChevronsUp size={14} />
          </IconButton>
          <IconButton
            label="Subir"
            disabled={!canMoveUp || busyId === `job-order-${job.id}`}
            onClick={() => onMove(job.id, "up")}
          >
            <ArrowUp size={14} />
          </IconButton>
          <IconButton
            label="Bajar"
            disabled={!canMoveDown || busyId === `job-order-${job.id}`}
            onClick={() => onMove(job.id, "down")}
          >
            <ArrowDown size={14} />
          </IconButton>
          <IconButton
            label="Ordenar cortes por material"
            disabled={busyId === `job-sort-${job.id}`}
            onClick={() => onSortByMaterial(job)}
          >
            {busyId === `job-sort-${job.id}` ? <Loader2 size={14} className="animate-spin" /> : <ListFilter size={14} />}
          </IconButton>
          {linkedPedido && (
            <Link
              href={`/pedidos/${linkedPedido}`}
              className="inline-flex h-8 items-center justify-center rounded-lg border border-line px-2.5 text-xs font-semibold text-ink-soft hover:text-ink"
            >
              Ver pedido
            </Link>
          )}
          <Button type="button" variant="secondary" size="sm" className="h-8 rounded-lg px-2 text-xs" onClick={() => onAddItem(job.id)}>
            + Corte
          </Button>
          <Button type="button" variant="secondary" size="sm" className="h-8 rounded-lg px-2 text-xs" onClick={() => onCopy(job)}>
            {copied ? <Check size={15} /> : <Copy size={15} />}
            {copied ? "Copiado" : "Copiar"}
          </Button>
          {canAdmin && (
            <Button type="button" variant="ghost" size="sm" className="h-8 rounded-lg px-2 text-xs" onClick={() => onArchive(job)} disabled={busyId === job.id}>
              <Archive size={15} /> Archivar
            </Button>
          )}
        </div>
      </header>

      {!collapsed && (
        <div className="divide-y divide-line">
          {job.items.length === 0 ? (
            <div className="px-3 py-2 text-xs text-faint">Sin cortes cargados.</div>
          ) : (
            orderedItems.map((item) => (
              <CutRow
                key={item.id}
                item={item}
                job={job}
                index={job.items.findIndex((i) => i.id === item.id)}
                busyId={busyId}
                onToggle={onToggle}
                onCycleCanto={onCycleCanto}
                onEdit={onEditItem}
                onDelete={onDeleteItem}
                onMove={onMoveItem}
              />
            ))
          )}
        </div>
      )}

      {/* Floating image tooltip on hover */}
      {imageTooltip && hasImage && pedido && (
        <div
          className="fixed z-50 pointer-events-none rounded-xl border border-line bg-surface p-2 shadow-xl"
          style={{ left: imageTooltip.x + 15, top: imageTooltip.y + 15 }}
        >
          {/* eslint-disable-next-line @next/next/no-img-element */}
          <img
            src={fileUrl(pedido, firstFoto, "300x0")}
            alt="Imagen del pedido"
            className="max-h-[160px] w-auto rounded-lg border border-line bg-canvas object-contain"
          />
        </div>
      )}

      {/* Full-size popup on click */}
      {imagePopup && hasImage && pedido && (
        <div
          className="fixed inset-0 z-[100] flex items-center justify-center bg-black/70"
          onClick={() => setImagePopup(false)}
        >
          <div className="relative" onClick={(e) => e.stopPropagation()}>
            {/* eslint-disable-next-line @next/next/no-img-element */}
            <img
              src={fileUrl(pedido, firstFoto, "1280x0")}
              alt="Imagen del pedido"
              className="max-h-[90vh] max-w-[90vw] rounded-xl border border-line object-contain shadow-2xl"
            />
            <button
              type="button"
              className="absolute right-2 top-2 flex h-7 w-7 items-center justify-center rounded-full bg-black/50 text-white hover:bg-black/70"
              onClick={() => setImagePopup(false)}
            >
              <X size={14} />
            </button>
          </div>
        </div>
      )}
    </section>
  );
}

function IconButton({
  label,
  disabled,
  onClick,
  children,
}: {
  label: string;
  disabled?: boolean;
  onClick: () => void;
  children: ReactNode;
}) {
  return (
    <button
      type="button"
      aria-label={label}
      title={label}
      disabled={disabled}
      onClick={onClick}
      className="inline-flex h-8 w-8 items-center justify-center rounded-lg border border-line bg-surface text-faint hover:text-ink disabled:cursor-not-allowed disabled:opacity-35"
    >
      {children}
    </button>
  );
}
function renderCutDimensions(item: CorteItem, isNota?: boolean): ReactNode {
  if (isNota) {
    if (item.material === "nota" || item.material === "herraje") {
      return item.nota || item.material;
    }
    return `${item.ancho}`; // pino (largo)
  }
  const renderDim = (val: number, hasVeta: boolean) => {
    if (hasVeta) {
      return (
        <span className="inline-flex items-center rounded-md bg-amber-50 border border-amber-200 px-1.5 py-0.5 text-xs font-extrabold text-amber-800 shadow-xs leading-none select-none">
          {val} veta
        </span>
      );
    }
    return <span>{val}</span>;
  };

  return (
    <span className="inline-flex items-center gap-1.5 align-middle">
      {renderDim(item.ancho, item.veta === "ancho")}
      <span className="text-faint font-normal select-none">x</span>
      {renderDim(item.alto, item.veta === "alto")}
    </span>
  );
}

function CutRow({
  item,
  job,
  index,
  busyId,
  onToggle,
  onCycleCanto,
  onEdit,
  onDelete,
  onMove,
}: {
  item: CorteItem;
  job: CorteJobWithItems;
  index: number;
  busyId: string | null;
  onToggle: (item: CorteItem) => void;
  onCycleCanto: (item: CorteItem, dim: "ancho" | "alto") => void;
  onEdit?: (item: CorteItem) => void;
  onDelete?: (item: CorteItem) => void;
  onMove?: (job: CorteJobWithItems, itemId: string, direction: "up" | "down") => void;
}) {
  const done = isCutItemComplete(item);
  const hasCanto = matHasCanto(item.material) && (item.canto_ancho > 0 || item.canto_alto > 0);
  const matGroup = getMatGroup(item.material);
  const badgeClass = matGroup?.badgeClass || "bg-canvas text-ink-soft border-line";

  return (
    <div className={cn("grid grid-cols-[auto_minmax(0,1fr)] gap-x-2 gap-y-1 px-2.5 py-1.5 md:grid-cols-[auto_minmax(0,1fr)_auto_auto] md:items-center", done && "bg-brand-soft/40")}>
      <button
        type="button"
        disabled={busyId === item.id}
        onClick={() => onToggle(item)}
        className={cn(
          "h-8 w-8 rounded-md border text-[11px] font-extrabold",
          item.estado === "cortado"
            ? "border-brand bg-brand text-white"
            : "border-line bg-canvas text-faint hover:text-ink",
        )}
        title={item.estado === "cortado" ? "Marcar pendiente" : "Marcar cortado"}
      >
        {busyId === item.id ? <Loader2 className="mx-auto h-3.5 w-3.5 animate-spin" /> : item.estado === "cortado" ? "OK" : ""}
      </button>
 
      <div className="min-w-0">
        <div className="flex flex-wrap items-center gap-1.5">
          <span className="font-mono text-sm font-extrabold leading-6 text-ink">
            {item.cantidad} x {renderCutDimensions(item, matGroup?.isNota)}
          </span>
          <span
            className="rounded-md border px-1.5 py-0.5 text-[11px] font-bold leading-none"
            style={(() => {
              const s = getMaterialColorStyle(item.material, item.color);
              return { backgroundColor: s.bg, color: s.text, borderColor: s.border };
            })()}
          >
            {materialLabel(item.material, item.color)}
          </span>
        </div>
        {item.nota && item.material !== "nota" && item.material !== "herraje" && (
          <p className="text-xs leading-5 text-ink-soft">{item.nota}</p>
        )}
      </div>

      {hasCanto && (
        <div className="col-start-2 flex items-center gap-1 md:col-start-auto">
          <CantoButton
            label="A"
            required={item.canto_ancho}
            done={item.canto_ancho_hecho}
            busy={busyId === `${item.id}-ancho`}
            onClick={() => onCycleCanto(item, "ancho")}
          />
          <CantoButton
            label="B"
            required={item.canto_alto}
            done={item.canto_alto_hecho}
            busy={busyId === `${item.id}-alto`}
            onClick={() => onCycleCanto(item, "alto")}
          />
        </div>
      )}
      {(onEdit || onDelete || onMove) && (
        <div className="col-start-2 flex items-center gap-1 md:col-start-auto">
          {onMove && (
            <>
              <IconButton label="Subir corte" disabled={index === 0 || busyId === `item-order-${item.id}`} onClick={() => onMove(job, item.id, "up")}>
                <ArrowUp size={13} />
              </IconButton>
              <IconButton label="Bajar corte" disabled={index === job.items.length - 1 || busyId === `item-order-${item.id}`} onClick={() => onMove(job, item.id, "down")}>
                <ArrowDown size={13} />
              </IconButton>
            </>
          )}
          {onEdit && (
            <IconButton label="Editar corte" onClick={() => onEdit(item)}>
              <Pencil size={13} />
            </IconButton>
          )}
          {onDelete && (
            <IconButton label="Borrar corte" disabled={busyId === `item-delete-${item.id}`} onClick={() => onDelete(item)}>
              {busyId === `item-delete-${item.id}` ? <Loader2 size={13} className="animate-spin" /> : <Trash2 size={13} />}
            </IconButton>
          )}
        </div>
      )}
    </div>
  );
}

function CantoButton({
  label,
  required,
  done,
  busy,
  onClick,
}: {
  label: string;
  required: number;
  done: number;
  busy: boolean;
  onClick: () => void;
}) {
  if (!required) return null;
  const complete = done >= required;
  return (
    <button
      type="button"
      onClick={onClick}
      className={cn(
        "h-7 min-w-10 rounded-md border px-1.5 text-[11px] font-bold",
        complete ? "border-brand bg-brand-soft text-brand-dark" : "border-line bg-canvas text-ink-soft",
      )}
      title={`Canto ${label}`}
    >
      {busy ? <Loader2 className="mx-auto h-4 w-4 animate-spin" /> : `${label} ${done}/${required}`}
    </button>
  );
}

function MaterialView({
  jobs,
  busyId,
  onToggle,
  onCycleCanto,
}: {
  jobs: CorteJobWithItems[];
  busyId: string | null;
  onToggle: (item: CorteItem) => void;
  onCycleCanto: (item: CorteItem, dim: "ancho" | "alto") => void;
}) {
  const jobMap = new Map(jobs.map((job) => [job.id, job]));
  const groups = groupItemsByMaterial(jobs.flatMap((job) => job.items));
  if (!groups.length) return <EmptyState text="No hay cortes pendientes por material." />;
  return (
    <div className="space-y-2">
      {groups.map((group) => (
        <section key={group.key} className="overflow-hidden rounded-lg border border-line bg-surface">
          <header className="border-b border-line px-3 py-2">
            <span
              className="rounded-md border px-1.5 py-0.5 text-[11px] font-bold leading-none"
              style={(() => {
                const parts = group.key.split(":");
                const s = getMaterialColorStyle(parts[0], parts[1]);
                return { backgroundColor: s.bg, color: s.text, borderColor: s.border };
              })()}
            >
              {group.label}
            </span>
            <span className="ml-2 text-xs font-medium text-faint">{group.items.length} corte{group.items.length !== 1 ? "s" : ""}</span>
          </header>
          <div className="divide-y divide-line">
            {group.items.map((item) => {
              const job = jobMap.get(item.job);
              return (
                <div key={item.id}>
                  <div className="px-3 pt-2 text-[11px] font-bold uppercase text-faint">
                    {job?.numero ? `#${job.numero}` : "Sin numero"} · {job?.cliente_nombre || "Sin cliente"}
                  </div>
                  {job && (
                    <CutRow
                      item={item}
                      job={job}
                      index={job.items.findIndex((candidate) => candidate.id === item.id)}
                      busyId={busyId}
                      onToggle={onToggle}
                      onCycleCanto={onCycleCanto}
                    />
                  )}
                </div>
              );
            })}
          </div>
        </section>
      ))}
    </div>
  );
}

function OptimizerView({ jobs }: { jobs: CorteJobWithItems[] }) {
  const groups = groupItemsByMaterial(jobs.flatMap((job) => job.items))
    .filter((group) => group.items.some((item) => item.ancho > 0 && item.alto > 0));
  if (!groups.length) return <EmptyState text="No hay placas pendientes para optimizar." />;
  return (
    <div className="space-y-3">
      {groups.map((group) => {
        const boards = packCuts(group.items);
        if (!boards.length) return null;
        return (
          <section key={group.key} className="rounded-lg border border-line bg-surface p-3">
            <div className="mb-2 flex items-center justify-between gap-3">
              <span
                className="rounded-md border px-1.5 py-0.5 text-[11px] font-bold leading-none"
                style={(() => {
                  const parts = group.key.split(":");
                  const s = getMaterialColorStyle(parts[0], parts[1]);
                  return { backgroundColor: s.bg, color: s.text, borderColor: s.border };
                })()}
              >
                {group.label}
              </span>
              <span className="text-xs font-semibold text-ink-soft">
                {boards.length} placa{boards.length !== 1 ? "s" : ""}
              </span>
            </div>
            <div className="grid gap-3 lg:grid-cols-2">
              {boards.map((board, index) => (
                <BoardPreview key={index} board={board} index={index} />
              ))}
            </div>
          </section>
        );
      })}
    </div>
  );
}

function BoardPreview({ board, index }: { board: PackedBoard; index: number }) {
  return (
    <div>
      <div className="mb-1.5 text-[11px] font-bold uppercase text-faint">
        {board.isRemnant ? "Recorte" : `Placa ${index + 1}`} · {board.width} x {board.height}
      </div>
      <svg viewBox={`0 0 ${board.width} ${board.height}`} className="w-full rounded-lg border border-line bg-canvas">
        <rect width={board.width} height={board.height} fill="#fffdf7" stroke="#ddd3bf" strokeWidth="0.8" />
        {board.placements.map((placement, i) => {
          const color = BOARD_COLORS[i % BOARD_COLORS.length];
          const fs = Math.max(4.2, Math.min(placement.width / 7, placement.height / 4, 8.8));
          return (
            <g key={`${placement.item.id}-${placement.pieceIndex}`}>
              <rect
                x={placement.x}
                y={placement.y}
                width={placement.width}
                height={placement.height}
                fill={`${color}28`}
                stroke={color}
                strokeWidth="1.0"
              />
              <text
                x={placement.x + placement.width / 2}
                y={placement.y + placement.height / 2}
                textAnchor="middle"
                dominantBaseline="middle"
                fill={color}
                fontSize={fs}
                fontWeight="700"
              >
                {placement.item.ancho}x{placement.item.alto}
              </text>
            </g>
          );
        })}
      </svg>
    </div>
  );
}

function EmptyState({ text = "No hay trabajos de corte cargados." }: { text?: string }) {
  return (
    <div className="grid place-items-center rounded-lg border border-dashed border-line bg-surface px-4 py-10 text-center">
      <div>
        <Scissors className="mx-auto mb-2 h-8 w-8 text-faint opacity-40" />
        <p className="text-sm text-faint">{text}</p>
        <Link href="/pedidos/nuevo" className="mt-3 inline-flex">
          <Button type="button" className="h-9 rounded-lg px-3 text-sm">
            Cargar pedido
          </Button>
        </Link>
      </div>
    </div>
  );
}

function ArchivedJobCard({ job }: { job: CorteJobWithItems }) {
  const progress = summarizeJobProgress(job.items);
  return (
    <article className="rounded-lg border border-line bg-canvas px-3 py-2">
      <div className="flex items-start justify-between gap-3">
        <div className="min-w-0">
          <div className="truncate text-sm font-extrabold text-ink">
            {job.numero ? `#${job.numero}` : "Sin numero"}
          </div>
          <p className="truncate text-xs text-ink-soft">{job.cliente_nombre || "Sin cliente"}</p>
        </div>
        <span className="shrink-0 rounded-full bg-zinc-100 px-2 py-0.5 text-[11px] font-bold text-zinc-700">
          {progress.complete}/{progress.total}
        </span>
      </div>
      <p className="mt-1 line-clamp-2 text-xs text-faint">
        {job.items.map((item) => `${item.cantidad}x ${materialLabel(item.material, item.color)}`).join(" · ") || "Sin cortes"}
      </p>
      {job.pedido && (
        <Link href={`/pedidos/${job.pedido}`} className="mt-2 inline-flex text-xs font-semibold text-brand-dark hover:underline">
          Ver pedido
        </Link>
      )}
    </article>
  );
}
