"use client";

import Link from "next/link";
import { useEffect, useRef, useState } from "react";
import { useRouter } from "next/navigation";
import {
  Badge,
  Button,
  Card,
  ErrorBanner,
  Field,
  Input,
  Modal,
  PageHeader,
  Select,
  Textarea,
} from "@/components/ui";
import { apiFetch, ApiClientError } from "@/lib/client-api";

interface FileView {
  id: string;
  originalName: string;
  sizeBytes: number;
  kind: string;
  mimeType: string | null;
}

interface PartInfo {
  id: string;
  code: string;
  name: string;
  material: string | null;
  qty: number;
  notes: string | null;
  stepFile: FileView | null;
  drawingFile: FileView | null;
  cutFile: FileView | null;
}

export interface OperationRow {
  id: string;
  sequence: number;
  name: string;
  machineId: string | null;
  machineName: string | null;
  templateId: string | null;
  templateName: string | null;
  settings: string | null;
  materialNotes: string | null;
  instructions: string | null;
  qcRequired: boolean;
  plannedMinutes: number | null;
  plannedUnits: number | null;
  status: string;
  qrToken: string;
}

interface MachineOption {
  id: string;
  name: string;
}

interface TemplateOption {
  id: string;
  name: string;
  machineId: string | null;
  defaultSettings: string | null;
  defaultInstructions: string | null;
  qcRequired: boolean;
}

type Slot = "stepFileId" | "drawingFileId" | "cutFileId";

const STATUS_TONE: Record<string, "slate" | "blue" | "green" | "amber" | "red"> = {
  pending: "slate",
  in_progress: "blue",
  completed: "green",
};

const STATUS_LABEL: Record<string, string> = {
  pending: "Pending",
  in_progress: "In progress",
  completed: "Completed",
};

function formatBytes(n: number) {
  if (n < 1024) return `${n} B`;
  if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`;
  return `${(n / (1024 * 1024)).toFixed(1)} MB`;
}

export default function PartDetailClient({
  project,
  assembly,
  part,
  operations,
  machines,
  templates,
}: {
  project: { id: string; code: string; name: string };
  assembly: { id: string; code: string; name: string };
  part: PartInfo;
  operations: OperationRow[];
  machines: MachineOption[];
  templates: TemplateOption[];
}) {
  const router = useRouter();
  const [editOpen, setEditOpen] = useState(false);

  return (
    <div className="mx-auto max-w-6xl px-4 py-8">
      <nav className="mb-3 text-sm text-slate-500">
        <Link href="/admin/projects" className="hover:underline">
          Projects
        </Link>
        <span className="mx-1">›</span>
        <Link href={`/admin/projects/${project.id}`} className="hover:underline">
          {project.code}
        </Link>
        <span className="mx-1">›</span>
        <Link
          href={`/admin/projects/${project.id}/assemblies/${assembly.id}`}
          className="hover:underline"
        >
          {assembly.code}
        </Link>
        <span className="mx-1">›</span>
        <span>{part.code}</span>
      </nav>
      <PageHeader
        title={
          <span className="flex items-center gap-3">
            <span className="font-mono text-slate-500 text-lg">{part.code}</span>
            <span>{part.name}</span>
          </span>
        }
        description={
          <span className="text-slate-500">
            {part.material ? `${part.material} · ` : ""}Qty {part.qty}
          </span>
        }
        actions={
          <Button variant="secondary" onClick={() => setEditOpen(true)}>
            Edit part
          </Button>
        }
      />

      {part.notes ? (
        <Card className="mb-4">
          <div className="p-4 text-sm text-slate-700 whitespace-pre-wrap">{part.notes}</div>
        </Card>
      ) : null}

      <section className="mb-8">
        <h2 className="text-lg font-semibold mb-3">Files</h2>
        <div className="grid gap-3 md:grid-cols-3">
          <FileSlot
            partId={part.id}
            label="STEP / 3D"
            description="3D model for in-app viewer."
            accept=".step,.stp"
            slot="stepFileId"
            file={part.stepFile}
            onChange={() => router.refresh()}
          />
          <FileSlot
            partId={part.id}
            label="Drawing (PDF)"
            description="Dimensioned drawing."
            accept=".pdf"
            slot="drawingFileId"
            file={part.drawingFile}
            onChange={() => router.refresh()}
          />
          <FileSlot
            partId={part.id}
            label="Cut file"
            description="DXF or SVG for the punch/laser."
            accept=".dxf,.svg"
            slot="cutFileId"
            file={part.cutFile}
            onChange={() => router.refresh()}
          />
        </div>
      </section>

      <OperationsSection
        partId={part.id}
        operations={operations}
        machines={machines}
        templates={templates}
        onChange={() => router.refresh()}
      />

      {editOpen && (
        <EditPartModal
          part={part}
          onClose={() => setEditOpen(false)}
          onSaved={() => router.refresh()}
          onDeleted={() => router.push(`/admin/projects/${project.id}/assemblies/${assembly.id}`)}
        />
      )}
    </div>
  );
}

// -------- Operations -----------------------------------------------------

function OperationsSection({
  partId,
  operations,
  machines,
  templates,
  onChange,
}: {
  partId: string;
  operations: OperationRow[];
  machines: MachineOption[];
  templates: TemplateOption[];
  onChange: () => void;
}) {
  const [newOpen, setNewOpen] = useState(false);
  const [edit, setEdit] = useState<OperationRow | null>(null);
  const [qrFor, setQrFor] = useState<OperationRow | null>(null);
  const [busyId, setBusyId] = useState<string | null>(null);
  const [error, setError] = useState<string | null>(null);

  async function move(index: number, dir: -1 | 1) {
    const target = index + dir;
    if (target < 0 || target >= operations.length) return;
    const order = operations.map((o) => o.id);
    [order[index], order[target]] = [order[target], order[index]];
    setBusyId(operations[index].id);
    setError(null);
    try {
      await apiFetch(`/api/v1/parts/${partId}/operations/reorder`, {
        method: "POST",
        body: JSON.stringify({ order }),
      });
      onChange();
    } catch (err) {
      setError(err instanceof ApiClientError ? err.message : "Reorder failed");
    } finally {
      setBusyId(null);
    }
  }

  async function remove(op: OperationRow) {
    if (!confirm(`Delete operation ${op.sequence}. ${op.name}?`)) return;
    setBusyId(op.id);
    setError(null);
    try {
      await apiFetch(`/api/v1/operations/${op.id}`, { method: "DELETE" });
      onChange();
    } catch (err) {
      setError(err instanceof ApiClientError ? err.message : "Delete failed");
    } finally {
      setBusyId(null);
    }
  }

  return (
    <section>
      <div className="flex items-center justify-between mb-3">
        <h2 className="text-lg font-semibold">Operations</h2>
        <Button size="sm" onClick={() => setNewOpen(true)}>
          Add operation
        </Button>
      </div>
      <ErrorBanner message={error} />
      <Card>
        <table className="w-full text-sm">
          <thead className="bg-slate-50 text-left text-slate-600 border-b border-slate-200">
            <tr>
              <th className="px-3 py-2 font-medium w-10">#</th>
              <th className="px-3 py-2 font-medium">Name</th>
              <th className="px-3 py-2 font-medium">Machine</th>
              <th className="px-3 py-2 font-medium">QC</th>
              <th className="px-3 py-2 font-medium">Status</th>
              <th className="px-3 py-2 font-medium">QR</th>
              <th className="px-3 py-2 text-right"></th>
            </tr>
          </thead>
          <tbody>
            {operations.map((op, i) => (
              <tr key={op.id} className="border-b border-slate-100 last:border-0">
                <td className="px-3 py-3 text-slate-600">{op.sequence}</td>
                <td className="px-3 py-3">
                  <div className="font-medium">{op.name}</div>
                  {op.templateName ? (
                    <div className="text-xs text-slate-500">from {op.templateName}</div>
                  ) : null}
                </td>
                <td className="px-3 py-3 text-slate-600">{op.machineName ?? "—"}</td>
                <td className="px-3 py-3">
                  {op.qcRequired ? <Badge tone="amber">required</Badge> : <span className="text-slate-400">—</span>}
                </td>
                <td className="px-3 py-3">
                  <Badge tone={STATUS_TONE[op.status] ?? "slate"}>
                    {STATUS_LABEL[op.status] ?? op.status}
                  </Badge>
                </td>
                <td className="px-3 py-3">
                  <code className="text-xs text-slate-500" title={op.qrToken}>
                    {op.qrToken.slice(0, 8)}…
                  </code>
                </td>
                <td className="px-3 py-3 text-right whitespace-nowrap">
                  <button
                    type="button"
                    className="text-slate-500 hover:text-slate-900 px-1 disabled:opacity-40"
                    disabled={i === 0 || busyId !== null}
                    onClick={() => move(i, -1)}
                    aria-label="Move up"
                  >
                    ↑
                  </button>
                  <button
                    type="button"
                    className="text-slate-500 hover:text-slate-900 px-1 disabled:opacity-40"
                    disabled={i === operations.length - 1 || busyId !== null}
                    onClick={() => move(i, 1)}
                    aria-label="Move down"
                  >
                    ↓
                  </button>
                  <Button variant="ghost" size="sm" onClick={() => setQrFor(op)} disabled={busyId !== null}>
                    QR
                  </Button>
                  <Button variant="ghost" size="sm" onClick={() => setEdit(op)} disabled={busyId !== null}>
                    Edit
                  </Button>
                  <Button
                    variant="ghost"
                    size="sm"
                    onClick={() => remove(op)}
                    disabled={busyId !== null || op.status === "in_progress"}
                  >
                    Delete
                  </Button>
                </td>
              </tr>
            ))}
            {operations.length === 0 && (
              <tr>
                <td colSpan={7} className="px-4 py-10 text-center text-slate-500">
                  No operations on this part yet.
                </td>
              </tr>
            )}
          </tbody>
        </table>
      </Card>

      {newOpen && (
        <OperationModal
          partId={partId}
          machines={machines}
          templates={templates}
          onClose={() => setNewOpen(false)}
          onSaved={() => {
            setNewOpen(false);
            onChange();
          }}
        />
      )}
      {edit && (
        <OperationModal
          partId={partId}
          operation={edit}
          machines={machines}
          templates={templates}
          onClose={() => setEdit(null)}
          onSaved={() => {
            setEdit(null);
            onChange();
          }}
        />
      )}
      {qrFor && <QrModal operation={qrFor} onClose={() => setQrFor(null)} />}
    </section>
  );
}

function QrModal({ operation, onClose }: { operation: OperationRow; onClose: () => void }) {
  const [data, setData] = useState<
    { dataUrl: string; scanUrl: string; token: string } | null
  >(null);
  const [error, setError] = useState<string | null>(null);

  // Fetch lazily so we don't pre-render QRs for every op on the page. The
  // data URL is ~1 KB so this is cheap, but it does require a server hop.
  useEffect(() => {
    let cancelled = false;
    apiFetch<{ dataUrl: string; scanUrl: string; token: string }>(
      `/api/v1/operations/${operation.id}/qr`,
    )
      .then((d) => {
        if (!cancelled) setData(d);
      })
      .catch((err) => {
        if (!cancelled) setError(err instanceof ApiClientError ? err.message : "Load failed");
      });
    return () => {
      cancelled = true;
    };
  }, [operation.id]);

  return (
    <Modal
      open
      onClose={onClose}
      title={`QR: step ${operation.sequence}. ${operation.name}`}
      footer={
        <>
          <div className="flex-1" />
          <Button variant="secondary" onClick={onClose}>
            Close
          </Button>
          {data ? (
            <a
              href={data.dataUrl}
              download={`op-${operation.sequence}-${operation.id}.png`}
              className="inline-flex items-center rounded-md bg-slate-900 text-white text-sm px-3 py-1.5 hover:bg-slate-800"
            >
              Download PNG
            </a>
          ) : null}
        </>
      }
    >
      <div className="space-y-3">
        {error ? (
          <ErrorBanner message={error} />
        ) : data ? (
          <>
            <div className="flex justify-center bg-white border border-slate-200 rounded-md p-4">
              {/* eslint-disable-next-line @next/next/no-img-element */}
              <img
                src={data.dataUrl}
                alt={`QR for ${operation.name}`}
                width={256}
                height={256}
              />
            </div>
            <div className="text-xs text-slate-600 space-y-1">
              <div>
                <span className="font-medium">Scan URL:</span>{" "}
                <a href={data.scanUrl} className="text-blue-600 hover:underline break-all">
                  {data.scanUrl}
                </a>
              </div>
              <div>
                <span className="font-medium">Token:</span>{" "}
                <code className="text-slate-500">{data.token}</code>
              </div>
            </div>
          </>
        ) : (
          <div className="text-center text-slate-500 text-sm py-10">Rendering QR…</div>
        )}
      </div>
    </Modal>
  );
}

function OperationModal({
  partId,
  operation,
  machines,
  templates,
  onClose,
  onSaved,
}: {
  partId: string;
  operation?: OperationRow;
  machines: MachineOption[];
  templates: TemplateOption[];
  onClose: () => void;
  onSaved: () => void;
}) {
  const editing = !!operation;
  const [templateId, setTemplateId] = useState(operation?.templateId ?? "");
  const [name, setName] = useState(operation?.name ?? "");
  const [machineId, setMachineId] = useState(operation?.machineId ?? "");
  const [settings, setSettings] = useState(operation?.settings ?? "");
  const [materialNotes, setMaterialNotes] = useState(operation?.materialNotes ?? "");
  const [instructions, setInstructions] = useState(operation?.instructions ?? "");
  const [qcRequired, setQcRequired] = useState(operation?.qcRequired ?? false);
  const [plannedMinutes, setPlannedMinutes] = useState(
    operation?.plannedMinutes ? String(operation.plannedMinutes) : "",
  );
  const [plannedUnits, setPlannedUnits] = useState(
    operation?.plannedUnits ? String(operation.plannedUnits) : "",
  );
  const [status, setStatus] = useState(operation?.status ?? "pending");
  const [busy, setBusy] = useState(false);
  const [error, setError] = useState<string | null>(null);

  function applyTemplate(id: string) {
    setTemplateId(id);
    if (!id) return;
    const t = templates.find((x) => x.id === id);
    if (!t) return;
    if (!name) setName(t.name);
    if (!machineId && t.machineId) setMachineId(t.machineId);
    if (!settings && t.defaultSettings) setSettings(t.defaultSettings);
    if (!instructions && t.defaultInstructions) setInstructions(t.defaultInstructions);
    if (t.qcRequired) setQcRequired(true);
  }

  async function submit(e: React.FormEvent) {
    e.preventDefault();
    setBusy(true);
    setError(null);
    try {
      const body = {
        templateId: templateId || null,
        name,
        machineId: machineId || null,
        settings,
        materialNotes,
        instructions,
        qcRequired,
        plannedMinutes: plannedMinutes ? Number(plannedMinutes) : null,
        plannedUnits: plannedUnits ? Number(plannedUnits) : null,
        ...(editing ? { status } : {}),
      };
      if (editing) {
        await apiFetch(`/api/v1/operations/${operation!.id}`, {
          method: "PATCH",
          body: JSON.stringify(body),
        });
      } else {
        await apiFetch(`/api/v1/parts/${partId}/operations`, {
          method: "POST",
          body: JSON.stringify(body),
        });
      }
      onSaved();
    } catch (err) {
      setError(err instanceof ApiClientError ? err.message : "Save failed");
      setBusy(false);
    }
  }

  return (
    <Modal
      open
      onClose={onClose}
      title={editing ? `Edit operation ${operation!.sequence}. ${operation!.name}` : "New operation"}
      footer={
        <>
          <div className="flex-1" />
          <Button variant="secondary" onClick={onClose} disabled={busy}>
            Cancel
          </Button>
          <Button type="submit" form="op-form" disabled={busy}>
            {busy ? "Saving…" : editing ? "Save" : "Create"}
          </Button>
        </>
      }
    >
      <form id="op-form" onSubmit={submit} className="space-y-4">
        {!editing && (
          <Field label="Start from template" hint="Pre-fills machine, settings, instructions, and QC.">
            <Select value={templateId} onChange={(e) => applyTemplate(e.target.value)}>
              <option value="">— ad-hoc, no template —</option>
              {templates.map((t) => (
                <option key={t.id} value={t.id}>
                  {t.name}
                </option>
              ))}
            </Select>
          </Field>
        )}
        <Field label="Name" required>
          <Input value={name} onChange={(e) => setName(e.target.value)} required />
        </Field>
        <Field label="Machine">
          <Select value={machineId} onChange={(e) => setMachineId(e.target.value)}>
            <option value="">— none —</option>
            {machines.map((m) => (
              <option key={m.id} value={m.id}>
                {m.name}
              </option>
            ))}
          </Select>
        </Field>
        <div className="grid gap-3 sm:grid-cols-2">
          <Field label="Planned minutes" hint="Optional estimate.">
            <Input
              type="number"
              min={1}
              value={plannedMinutes}
              onChange={(e) => setPlannedMinutes(e.target.value)}
            />
          </Field>
          <Field label="Planned units" hint="Target count for this step.">
            <Input
              type="number"
              min={1}
              value={plannedUnits}
              onChange={(e) => setPlannedUnits(e.target.value)}
            />
          </Field>
        </div>
        <Field label="Settings (JSON)" hint='e.g. {"programNumber":47}. Optional.'>
          <Textarea
            value={settings}
            onChange={(e) => setSettings(e.target.value)}
            placeholder='{"programNumber":47}'
          />
        </Field>
        <Field label="Material notes" hint="Stock sizing, grain direction, etc.">
          <Textarea value={materialNotes} onChange={(e) => setMaterialNotes(e.target.value)} />
        </Field>
        <Field label="Instructions" hint="Printed on the traveler card.">
          <Textarea value={instructions} onChange={(e) => setInstructions(e.target.value)} />
        </Field>
        <label className="flex items-center gap-2 text-sm">
          <input type="checkbox" checked={qcRequired} onChange={(e) => setQcRequired(e.target.checked)} />
          <span>QC check required on close-out</span>
        </label>
        {editing && (
          <Field label="Status">
            <Select value={status} onChange={(e) => setStatus(e.target.value)}>
              <option value="pending">Pending</option>
              <option value="in_progress">In progress</option>
              <option value="completed">Completed</option>
            </Select>
          </Field>
        )}
        <ErrorBanner message={error} />
      </form>
    </Modal>
  );
}

// -------- Files ----------------------------------------------------------

function FileSlot({
  partId,
  label,
  description,
  accept,
  slot,
  file,
  onChange,
}: {
  partId: string;
  label: string;
  description: string;
  accept: string;
  slot: Slot;
  file: FileView | null;
  onChange: () => void;
}) {
  const inputRef = useRef<HTMLInputElement>(null);
  const [busy, setBusy] = useState(false);
  const [error, setError] = useState<string | null>(null);

  async function upload(f: File) {
    setBusy(true);
    setError(null);
    try {
      const form = new FormData();
      form.set("file", f);
      const uploaded = await apiFetch<{ file: { id: string } }>("/api/v1/files", {
        method: "POST",
        body: form,
      });
      await apiFetch(`/api/v1/parts/${partId}`, {
        method: "PATCH",
        body: JSON.stringify({ [slot]: uploaded.file.id }),
      });
      onChange();
    } catch (err) {
      setError(err instanceof ApiClientError ? err.message : "Upload failed");
    } finally {
      setBusy(false);
      if (inputRef.current) inputRef.current.value = "";
    }
  }

  async function detach() {
    if (!confirm(`Detach ${label}?`)) return;
    setBusy(true);
    setError(null);
    try {
      await apiFetch(`/api/v1/parts/${partId}`, {
        method: "PATCH",
        body: JSON.stringify({ [slot]: null }),
      });
      onChange();
    } catch (err) {
      setError(err instanceof ApiClientError ? err.message : "Detach failed");
    } finally {
      setBusy(false);
    }
  }

  return (
    <Card>
      <div className="p-4 space-y-3">
        <div>
          <h3 className="font-semibold">{label}</h3>
          <p className="text-xs text-slate-500">{description}</p>
        </div>
        {file ? (
          <div className="rounded-md border border-slate-200 bg-slate-50 p-3 text-sm">
            <div className="font-medium truncate" title={file.originalName}>
              {file.originalName}
            </div>
            <div className="text-xs text-slate-500 mt-0.5">
              {formatBytes(file.sizeBytes)} · {file.kind}
            </div>
            <div className="flex gap-2 mt-2">
              <a
                href={`/api/v1/files/${file.id}/download`}
                className="text-xs text-blue-600 hover:underline"
              >
                Download
              </a>
              <button
                type="button"
                onClick={detach}
                disabled={busy}
                className="text-xs text-red-600 hover:underline disabled:opacity-50"
              >
                Detach
              </button>
              <button
                type="button"
                onClick={() => inputRef.current?.click()}
                disabled={busy}
                className="text-xs text-slate-600 hover:underline disabled:opacity-50 ml-auto"
              >
                Replace
              </button>
            </div>
          </div>
        ) : (
          <div className="rounded-md border border-dashed border-slate-300 p-4 text-center">
            <p className="text-xs text-slate-500 mb-2">No file attached.</p>
            <Button
              type="button"
              size="sm"
              variant="secondary"
              onClick={() => inputRef.current?.click()}
              disabled={busy}
            >
              {busy ? "Uploading…" : "Upload"}
            </Button>
          </div>
        )}
        <input
          ref={inputRef}
          type="file"
          accept={accept}
          className="hidden"
          onChange={(e) => {
            const f = e.target.files?.[0];
            if (f) upload(f);
          }}
        />
        <ErrorBanner message={error} />
      </div>
    </Card>
  );
}

// -------- Edit part ------------------------------------------------------

function EditPartModal({
  part,
  onClose,
  onSaved,
  onDeleted,
}: {
  part: PartInfo;
  onClose: () => void;
  onSaved: () => void;
  onDeleted: () => void;
}) {
  const [code, setCode] = useState(part.code);
  const [name, setName] = useState(part.name);
  const [material, setMaterial] = useState(part.material ?? "");
  const [qty, setQty] = useState(String(part.qty));
  const [notes, setNotes] = useState(part.notes ?? "");
  const [busy, setBusy] = useState(false);
  const [error, setError] = useState<string | null>(null);

  async function submit(e: React.FormEvent) {
    e.preventDefault();
    setBusy(true);
    setError(null);
    try {
      await apiFetch(`/api/v1/parts/${part.id}`, {
        method: "PATCH",
        body: JSON.stringify({
          code,
          name,
          material: material || null,
          qty: Number(qty),
          notes: notes || null,
        }),
      });
      onSaved();
      onClose();
    } catch (err) {
      setError(err instanceof ApiClientError ? err.message : "Save failed");
      setBusy(false);
    }
  }

  async function remove() {
    if (!confirm(`Delete part ${part.code}? All operations on it will be removed.`)) return;
    setBusy(true);
    setError(null);
    try {
      await apiFetch(`/api/v1/parts/${part.id}`, { method: "DELETE" });
      onDeleted();
    } catch (err) {
      setError(err instanceof ApiClientError ? err.message : "Delete failed");
      setBusy(false);
    }
  }

  return (
    <Modal
      open
      onClose={onClose}
      title={`Edit ${part.code}`}
      footer={
        <>
          <Button variant="danger" size="sm" onClick={remove} disabled={busy}>
            Delete
          </Button>
          <div className="flex-1" />
          <Button variant="secondary" onClick={onClose} disabled={busy}>
            Cancel
          </Button>
          <Button type="submit" form="part-edit-form" disabled={busy}>
            {busy ? "Saving…" : "Save"}
          </Button>
        </>
      }
    >
      <form id="part-edit-form" onSubmit={submit} className="space-y-4">
        <Field label="Code" required>
          <Input value={code} onChange={(e) => setCode(e.target.value)} required />
        </Field>
        <Field label="Name" required>
          <Input value={name} onChange={(e) => setName(e.target.value)} required />
        </Field>
        <Field label="Material">
          <Input value={material} onChange={(e) => setMaterial(e.target.value)} />
        </Field>
        <Field label="Quantity" required>
          <Input type="number" min={1} value={qty} onChange={(e) => setQty(e.target.value)} required />
        </Field>
        <Field label="Notes">
          <Textarea value={notes} onChange={(e) => setNotes(e.target.value)} />
        </Field>
        <ErrorBanner message={error} />
      </form>
    </Modal>
  );
}
