"use client";

import { useMemo, useState } from "react";
import useSWR from "swr";
import AppShell from "@/components/AppShell";
import {
  ConfirmDialog,
  EmptyState,
  FormError,
  LoadError,
  Modal,
  PageHeader,
  SkeletonRows,
  Spinner,
} from "@/components/ui";
import { useToast } from "@/components/toast";
import { api, fetcher } from "@/lib/api";

type CatalogEntry = {
  key: string;
  label: string;
  description: string;
  device_type: string;
  mcp_server: string | null;
  default_port: number;
  default_username: string | null;
  has_default_secret: boolean;
  secret_kind: string;
  env_defaults_configured: boolean;
  asterisk_container?: string | null;
  category: string;
};

type Device = {
  id: number;
  name: string;
  catalog_key: string | null;
  device_type: string;
  address: string;
  port: number | null;
  mcp_server: string | null;
  username: string | null;
  has_secret: boolean;
  vessel_id: number | null;
};

type Vessel = {
  id: number;
  name: string;
  site: string | null;
  public_ip: string | null;
  notes: string | null;
  devices?: Device[];
};

type SlotState = {
  enabled: boolean;
  port: string;
  address: string;
  username: string;
  secret: string;
};

function defaultSlots(catalog: CatalogEntry[]): Record<string, SlotState> {
  return Object.fromEntries(
    catalog.map((c) => [
      c.key,
      {
        enabled: ["proxmox", "pfsense", "docker_vm", "asterisk_geneseasx"].includes(c.key),
        port: String(c.default_port),
        address: "",
        username: c.default_username || "",
        secret: "",
      },
    ])
  );
}

function secretPlaceholder(c: CatalogEntry, editing: boolean) {
  if (c.has_default_secret) return "configured in .env (leave blank to use)";
  if (c.secret_kind === "api_key") return editing ? "leave blank to keep" : "API key";
  return editing ? "leave blank to keep" : "password";
}

export default function InventoryPage() {
  const { data: catalog, error: catalogError } = useSWR<CatalogEntry[]>(
    "/api/vessels/catalog",
    fetcher
  );
  const { data: vessels, error: vesselsError, isLoading, mutate } = useSWR<Vessel[]>(
    "/api/vessels",
    fetcher
  );
  const toast = useToast();

  const [open, setOpen] = useState(false);
  const [editId, setEditId] = useState<number | null>(null);
  const [expanded, setExpanded] = useState<number | null>(null);
  const [loadingDetail, setLoadingDetail] = useState<number | null>(null);
  const [error, setError] = useState<string | null>(null);
  const [saving, setSaving] = useState(false);
  const [query, setQuery] = useState("");
  const [deleteId, setDeleteId] = useState<number | null>(null);
  const [deleting, setDeleting] = useState(false);

  const [form, setForm] = useState({ name: "", site: "", public_ip: "", notes: "" });
  const [slots, setSlots] = useState<Record<string, SlotState>>({});

  const filteredVessels = useMemo(() => {
    const q = query.trim().toLowerCase();
    if (!q) return vessels || [];
    return (vessels || []).filter(
      (v) =>
        v.name.toLowerCase().includes(q) ||
        (v.site || "").toLowerCase().includes(q) ||
        (v.public_ip || "").toLowerCase().includes(q)
    );
  }, [vessels, query]);

  const catalogSorted = useMemo(() => {
    if (!catalog) return [];
    const order = { core: 0, network: 1, optional: 2 };
    return [...catalog].sort(
      (a, b) => (order[a.category as keyof typeof order] ?? 9) - (order[b.category as keyof typeof order] ?? 9)
    );
  }, [catalog]);

  const openCreate = () => {
    setEditId(null);
    setForm({ name: "", site: "", public_ip: "", notes: "" });
    setSlots(catalog ? defaultSlots(catalog) : {});
    setError(null);
    setOpen(true);
  };

  const openEdit = async (id: number) => {
    setError(null);
    const detail = await api<Vessel>(`/api/vessels/${id}`);
    setEditId(id);
    setForm({
      name: detail.name,
      site: detail.site || "",
      public_ip: detail.public_ip || "",
      notes: detail.notes || "",
    });
    const base = catalog ? defaultSlots(catalog) : {};
    for (const d of detail.devices || []) {
      if (d.catalog_key && base[d.catalog_key]) {
        base[d.catalog_key] = {
          enabled: true,
          port: d.port != null ? String(d.port) : base[d.catalog_key].port,
          address: d.address !== detail.public_ip ? d.address : "",
          username: d.username || "",
          secret: "",
        };
      }
    }
    setSlots(base);
    setOpen(true);
  };

  const toggleSlot = (key: string, enabled: boolean) => {
    setSlots((s) => ({ ...s, [key]: { ...s[key], enabled } }));
  };

  const updateSlot = (key: string, patch: Partial<SlotState>) => {
    setSlots((s) => ({ ...s, [key]: { ...s[key], ...patch } }));
  };

  const buildPayload = () => {
    const devices = Object.entries(slots)
      .filter(([, v]) => v.enabled)
      .map(([catalog_key, v]) => ({
        catalog_key,
        enabled: true,
        port: v.port ? Number(v.port) : undefined,
        address: v.address || undefined,
        username: v.username || undefined,
        secret: v.secret || undefined,
      }));
    return { ...form, site: form.site || null, public_ip: form.public_ip || null, notes: form.notes || null, devices };
  };

  const submit = async (e: React.FormEvent) => {
    e.preventDefault();
    setError(null);
    setSaving(true);
    try {
      const payload = buildPayload();
      if (editId) {
        await api(`/api/vessels/${editId}`, { method: "PATCH", body: JSON.stringify(payload) });
      } else {
        await api("/api/vessels", { method: "POST", body: JSON.stringify(payload) });
      }
      setOpen(false);
      toast.success(editId ? "Vessel updated" : "Vessel created");
      mutate();
    } catch (err: any) {
      setError(err.message);
      toast.error(err.message || "Save failed");
    } finally {
      setSaving(false);
    }
  };

  const remove = async () => {
    if (deleteId == null) return;
    setDeleting(true);
    try {
      await api(`/api/vessels/${deleteId}`, { method: "DELETE" });
      toast.success("Vessel deleted");
      mutate();
    } catch (err: any) {
      toast.error(err.message || "Delete failed");
    } finally {
      setDeleting(false);
      setDeleteId(null);
    }
  };

  const loadDetail = async (id: number) => {
    if (expanded === id) {
      setExpanded(null);
      return;
    }
    setLoadingDetail(id);
    try {
      const detail = await api<Vessel>(`/api/vessels/${id}`);
      mutate(
        (current) => current?.map((v) => (v.id === id ? { ...v, devices: detail.devices } : v)),
        false
      );
      setExpanded(id);
    } catch (err: any) {
      toast.error(err.message || "Could not load vessel devices");
    } finally {
      setLoadingDetail(null);
    }
  };

  return (
    <AppShell>
      <PageHeader
        title="Vessels"
        subtitle="Each vessel is a site with a public IP and the devices you select from the catalog"
        action={
          <button className="btn-primary" onClick={openCreate} disabled={!catalog}>
            {catalog ? "+ New vessel" : "Loading catalog…"}
          </button>
        }
      />

      {vessels && vessels.length > 0 && (
        <input
          className="input mb-4 sm:max-w-xs"
          placeholder="Search name, site, or IP…"
          value={query}
          onChange={(e) => setQuery(e.target.value)}
          aria-label="Search vessels"
        />
      )}

      {isLoading && !vessels ? (
        <SkeletonRows rows={3} />
      ) : vesselsError ? (
        <LoadError message="Couldn’t load vessels." onRetry={() => mutate()} />
      ) : (vessels || []).length === 0 ? (
        <EmptyState
          title="No vessels yet"
          description="Create one and pick its devices from the catalog."
          action={
            <button className="btn-primary" onClick={openCreate} disabled={!catalog}>
              + New vessel
            </button>
          }
        />
      ) : filteredVessels.length === 0 ? (
        <EmptyState title="No matching vessels" description="Try a different search." />
      ) : (
      <div className="space-y-3">
        {filteredVessels.map((v) => (
          <div key={v.id} className="card">
            <div className="flex flex-col items-start justify-between gap-4 sm:flex-row sm:items-center">
              <button className="min-w-0 flex-1 text-left" onClick={() => loadDetail(v.id)}>
                <div className="flex items-center gap-2">
                  <span className="font-semibold">{v.name}</span>
                  {v.site && <span className="text-xs text-muted">{v.site}</span>}
                  {loadingDetail === v.id && <Spinner className="text-muted" />}
                </div>
                <div className="mt-1 font-mono text-xs text-accent2">{v.public_ip || "no public IP"}</div>
                {v.notes && <div className="mt-1 text-sm text-muted">{v.notes}</div>}
              </button>
              <div className="flex shrink-0 gap-2">
                <button className="btn-ghost btn-sm" onClick={() => openEdit(v.id)}>
                  Edit
                </button>
                <button className="btn-danger btn-sm" onClick={() => setDeleteId(v.id)}>
                  Delete
                </button>
              </div>
            </div>
            {expanded === v.id && v.devices && (
              <div className="mt-4 overflow-x-auto rounded-xl border border-edge">
                <table className="w-full min-w-[560px]">
                  <thead>
                    <tr>
                      <th className="th">Device</th>
                      <th className="th">Type</th>
                      <th className="th">Endpoint</th>
                      <th className="th">Port</th>
                      <th className="th">MCP</th>
                    </tr>
                  </thead>
                  <tbody>
                    {v.devices.map((d) => (
                      <tr key={d.id}>
                        <td className="td">{d.name}</td>
                        <td className="td text-xs">{d.catalog_key || d.device_type}</td>
                        <td className="td font-mono text-xs">{d.address}</td>
                        <td className="td font-mono text-xs">{d.port ?? "—"}</td>
                        <td className="td text-xs">{d.mcp_server || "—"}</td>
                      </tr>
                    ))}
                  </tbody>
                </table>
              </div>
            )}
          </div>
        ))}
      </div>
      )}

      <Modal open={open} onClose={() => setOpen(false)} title={editId ? "Edit vessel" : "New vessel"}>
        <form onSubmit={submit} className="space-y-4">
          <div className="grid grid-cols-1 gap-3 sm:grid-cols-2">
            <div>
              <label className="label">Vessel name</label>
              <input className="input" value={form.name} onChange={(e) => setForm({ ...form, name: e.target.value })} required />
            </div>
            <div>
              <label className="label">Site / region</label>
              <input className="input" value={form.site} onChange={(e) => setForm({ ...form, site: e.target.value })} />
            </div>
          </div>
          <div>
            <label className="label">Public IP (shared endpoint for port forwards)</label>
            <input
              className="input font-mono"
              value={form.public_ip}
              onChange={(e) => setForm({ ...form, public_ip: e.target.value })}
              placeholder="e.g. 203.0.113.10"
            />
          </div>
          <div>
            <label className="label">Notes</label>
            <textarea className="input" value={form.notes} onChange={(e) => setForm({ ...form, notes: e.target.value })} />
          </div>

          <div>
            <label className="label mb-2">Onboard devices</label>
            <p className="mb-3 text-xs text-muted">
              Check what this vessel has. Ports and credentials pre-fill from catalog defaults (.env when set).
              Address defaults to the vessel public IP.
            </p>
            <div className="space-y-2">
              {catalogSorted.map((c) => {
                const slot = slots[c.key];
                if (!slot) return null;
                return (
                  <div
                    key={c.key}
                    className={`rounded-xl border p-3 ${slot.enabled ? "border-accent/40 bg-panel2" : "border-edge/60"}`}
                  >
                    <label className="flex cursor-pointer items-start gap-3">
                      <input
                        type="checkbox"
                        className="mt-1"
                        checked={slot.enabled}
                        onChange={(e) => toggleSlot(c.key, e.target.checked)}
                      />
                      <div className="min-w-0 flex-1">
                        <div className="flex items-center gap-2">
                          <span className="font-medium">{c.label}</span>
                          <span className="badge bg-slate-500/20 text-slate-400">{c.category}</span>
                        </div>
                        <div className="text-xs text-muted">{c.description}</div>
                        {c.asterisk_container && (
                          <div className="mt-1 text-[11px] text-muted">
                            Docker container: <span className="font-mono">{c.asterisk_container}</span>
                          </div>
                        )}
                        {c.env_defaults_configured && (
                          <div className="mt-1 text-[11px] text-accent2">
                            .env defaults loaded
                            {c.has_default_secret ? ` · ${c.secret_kind === "api_key" ? "API key" : "password"} on file` : ""}
                          </div>
                        )}
                      </div>
                    </label>
                    {slot.enabled && (
                      <div className="mt-3 grid grid-cols-1 gap-2 pl-7 sm:grid-cols-2">
                        <div>
                          <label className="label">Port (default {c.default_port})</label>
                          <input
                            className="input font-mono"
                            value={slot.port}
                            onChange={(e) => updateSlot(c.key, { port: e.target.value })}
                          />
                        </div>
                        <div>
                          <label className="label">Address override</label>
                          <input
                            className="input font-mono text-xs"
                            placeholder="uses public IP if empty"
                            value={slot.address}
                            onChange={(e) => updateSlot(c.key, { address: e.target.value })}
                          />
                        </div>
                        <div>
                          <label className="label">Username</label>
                          <input
                            className="input"
                            placeholder={c.default_username || "optional"}
                            value={slot.username}
                            onChange={(e) => updateSlot(c.key, { username: e.target.value })}
                          />
                        </div>
                        <div>
                          <label className="label">
                            {c.secret_kind === "api_key" ? "API key" : "Password"}
                          </label>
                          <input
                            className="input"
                            type="password"
                            placeholder={secretPlaceholder(c, !!editId)}
                            value={slot.secret}
                            onChange={(e) => updateSlot(c.key, { secret: e.target.value })}
                          />
                        </div>
                      </div>
                    )}
                  </div>
                );
              })}
            </div>
          </div>

          <FormError message={error} />
          <button className="btn-primary w-full" disabled={saving}>
            {saving ? "Saving…" : editId ? "Save vessel" : "Create vessel"}
          </button>
        </form>
      </Modal>

      <ConfirmDialog
        open={deleteId != null}
        title="Delete vessel?"
        message="This deletes the vessel and all of its onboard devices."
        confirmLabel="Delete"
        destructive
        busy={deleting}
        onConfirm={remove}
        onCancel={() => setDeleteId(null)}
      />
    </AppShell>
  );
}
