"use client";

import { useEffect, useMemo, useState } from "react";
import {
  ArrowRight,
  Box,
  Boxes,
  ChevronDown,
  ChevronRight,
  Circle,
  FolderPlus,
  Package,
  Search,
} from "lucide-react";
import { useDiagramStore, type EntropykNodeData } from "@/store/diagramStore";
import { COMPONENTS, COMPONENT_CATEGORIES, type ComponentMeta } from "@/lib/componentMeta";
import { ComponentIcon } from "@/components/canvas/ComponentIcon";
import { hxFamily } from "@/lib/hxFamily";
import { fetchModules, type ModuleInfo } from "@/lib/api";

interface LeftNavProps {
  onSelectModuleForEdit?: (moduleName: string) => void;
  onEnterModule?: (moduleName: string, instanceName: string) => void;
  onOpenNewModelModal?: () => void;
  /** Currently open model tab (.ekmod), if any. */
  activeClassName?: string | null;
}

/**
 * Package browser — Modelica / OMEdit mental model:
 *
 *   Models (.ekmod)  = classes / modèles (comme Modelica)
 *   Diagram          = contenu du canvas ouvert (instances + parts)
 *   Parts            = composants atomiques
 *
 * Le Workspace est l’onglet canvas du haut — pas une section vide ici.
 */
export default function LeftNav({
  onSelectModuleForEdit,
  onEnterModule,
  onOpenNewModelModal,
  activeClassName = null,
}: LeftNavProps) {
  const [modelsOpen, setModelsOpen] = useState(true);
  const [diagramOpen, setDiagramOpen] = useState(true);
  const [partsOpen, setPartsOpen] = useState(true);
  const [query, setQuery] = useState("");

  const queryLower = query.trim().toLowerCase();
  const hasQuery = queryLower.length > 0;

  return (
    <aside className="flex h-full w-64 flex-col border-r border-[var(--line)] bg-[var(--panel)]">
      <div className="border-b border-[var(--line)] bg-[var(--chrome)] p-2">
        <div className="relative flex items-center">
          <Search size={12} className="absolute left-2.5 text-[var(--ink-faint)]" />
          <input
            type="text"
            value={query}
            onChange={(e) => setQuery(e.target.value)}
            placeholder="Rechercher modèles, diagramme, parts…"
            className="w-full rounded border border-[var(--line)] bg-white py-1 pl-7 pr-2 text-[11px] text-[var(--ink)] outline-none focus:border-[var(--accent)]"
          />
        </div>
      </div>

      <div className="flex min-h-0 flex-1 flex-col overflow-y-auto">
        {/* Models = Modelica classes — primary */}
        <ModelsSection
          open={modelsOpen || hasQuery}
          onToggle={() => setModelsOpen((v) => !v)}
          query={queryLower}
          activeClassName={activeClassName}
          onSelectModuleForEdit={onSelectModuleForEdit}
          onOpenNewModelModal={onOpenNewModelModal}
        />

        <Divider />

        {/* What's on the open canvas — not "Workspace" */}
        <DiagramSection
          open={diagramOpen || hasQuery}
          onToggle={() => setDiagramOpen((v) => !v)}
          query={queryLower}
          activeClassName={activeClassName}
          onEnterModule={onEnterModule}
        />

        <Divider />

        <PartsSection
          open={partsOpen || hasQuery}
          onToggle={() => setPartsOpen((v) => !v)}
          query={queryLower}
        />
      </div>
    </aside>
  );
}

function Divider() {
  return <div className="h-px bg-[var(--line)]" />;
}

function SectionHeader({
  open,
  onToggle,
  icon,
  label,
  count,
  trailing,
}: {
  open: boolean;
  onToggle: () => void;
  icon: React.ReactNode;
  label: string;
  count?: number;
  trailing?: React.ReactNode;
}) {
  return (
    <div className="flex h-8 items-center gap-1 border-b border-[var(--line)]/50 bg-[var(--chrome)] px-2">
      <button
        type="button"
        onClick={onToggle}
        className="flex min-w-0 flex-1 items-center gap-1 text-left hover:text-[var(--ink)]"
      >
        {open ? (
          <ChevronDown size={12} className="text-[var(--ink-faint)]" />
        ) : (
          <ChevronRight size={12} className="text-[var(--ink-faint)]" />
        )}
        <span className="flex h-4 w-4 items-center justify-center">{icon}</span>
        <span className="eyebrow">{label}</span>
        {typeof count === "number" && (
          <span className="mono ml-1 text-[9px] text-[var(--ink-faint)]">{count}</span>
        )}
      </button>
      {trailing}
    </div>
  );
}

/* ── Models = Modelica classes (.ekmod) ───────────────────────────── */

function ModelsSection({
  open,
  onToggle,
  query,
  activeClassName,
  onSelectModuleForEdit,
  onOpenNewModelModal,
}: {
  open: boolean;
  onToggle: () => void;
  query: string;
  activeClassName?: string | null;
  onSelectModuleForEdit?: (name: string) => void;
  onOpenNewModelModal?: () => void;
}) {
  const [modules, setModules] = useState<ModuleInfo[]>([]);
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    let active = true;
    const load = async () => {
      setLoading(true);
      try {
        const list = await fetchModules();
        if (active) setModules(list);
      } finally {
        if (active) setLoading(false);
      }
    };
    load();
    const handler = () => load();
    window.addEventListener("entropyk-modules-updated", handler);
    return () => {
      active = false;
      window.removeEventListener("entropyk-modules-updated", handler);
    };
  }, []);

  const filtered = useMemo(
    () =>
      modules.filter(
        (m) =>
          !query ||
          m.name.toLowerCase().includes(query) ||
          m.ports.some((p) => p.toLowerCase().includes(query)),
      ),
    [modules, query],
  );

  const onModuleDragStart = (e: React.DragEvent, moduleName: string, ports: string[]) => {
    e.dataTransfer.setData("application/entropyk-type", `ModuleInstance:${moduleName}`);
    e.dataTransfer.setData("application/entropyk-ports", JSON.stringify(ports));
    e.dataTransfer.effectAllowed = "move";
  };

  const dropOnCanvas = (moduleName: string, ports: string[]) => {
    useDiagramStore
      .getState()
      .addComponent(
        "ModuleInstance",
        { x: 280 + Math.random() * 40, y: 140 + Math.random() * 40 },
        { module_name: moduleName, module_ports: JSON.stringify(ports) },
      );
  };

  return (
    <div className="flex flex-col">
      <SectionHeader
        open={open}
        onToggle={onToggle}
        icon={<Package size={12} className="text-[var(--accent)]" />}
        label="Models"
        count={modules.length}
      />
      {open && (
        <div className="py-1.5">
          {onOpenNewModelModal && (
            <div className="px-2 pb-2">
              <button
                type="button"
                onClick={(e) => {
                  e.preventDefault();
                  e.stopPropagation();
                  onOpenNewModelModal();
                }}
                className="flex w-full items-center justify-center gap-1.5 rounded-md bg-[var(--accent)] px-3 py-2 text-[12px] font-semibold text-white shadow-sm hover:bg-[var(--accent-hover,#1d4ed8)] active:scale-[0.99]"
              >
                <FolderPlus size={14} /> Nouveau modèle
              </button>
            </div>
          )}
          <p className="px-3 pb-1.5 text-[10px] leading-snug text-[var(--ink-faint)]">
            Classes Modelica · .ekmod — double-clic pour éditer, glisser pour instancier
          </p>
          {loading && modules.length === 0 && (
            <p className="px-3 py-2 text-[11px] text-[var(--ink-faint)]">Chargement…</p>
          )}
          {!loading && filtered.length === 0 && (
            <p className="px-3 py-2 text-[11px] text-[var(--ink-faint)]">
              {query ? "Aucun modèle." : "Aucun modèle — clique Nouveau."}
            </p>
          )}
          {filtered.map((m) => {
            const nInst = m.n_instances || 0;
            const nComp = m.n_components || 0;
            const isActive = activeClassName === m.name;
            const meta =
              nInst > 0
                ? `${nInst} nested · ${nComp} parts`
                : nComp > 0
                  ? `${nComp} part${nComp > 1 ? "s" : ""}`
                  : "vide";
            return (
              <div
                key={m.name}
                draggable
                onDragStart={(e) => onModuleDragStart(e, m.name, m.ports)}
                onDoubleClick={() => onSelectModuleForEdit?.(m.name)}
                className={`group mx-1.5 mb-1.5 cursor-grab rounded-md border px-2.5 py-2 active:cursor-grabbing ${
                  isActive
                    ? "border-[var(--accent)] bg-[var(--accent-subtle,#eff6ff)]"
                    : "border-[var(--line)] bg-white hover:border-[var(--accent)]/40"
                }`}
                title={`${m.name}.ekmod`}
              >
                <div className="flex items-center gap-1.5">
                  <Package size={14} className="shrink-0 text-[var(--accent)]" />
                  <span className="mono min-w-0 flex-1 truncate text-[12px] font-semibold text-[var(--ink)]">
                    {m.name}
                  </span>
                  <span className="mono shrink-0 text-[10px] text-[var(--ink-faint)]">.ekmod</span>
                </div>
                <div className="mt-1 flex items-center justify-between gap-1 pl-[22px]">
                  <span className="mono text-[10px] text-[var(--ink-faint)]">
                    {meta}
                    {m.ports.length > 0 ? ` · ${m.ports.length} ports` : ""}
                  </span>
                  <div className="flex items-center gap-0.5 opacity-0 transition-opacity group-hover:opacity-100">
                    <button
                      type="button"
                      onClick={(e) => {
                        e.stopPropagation();
                        dropOnCanvas(m.name, m.ports);
                      }}
                      className="rounded px-1.5 py-0.5 text-[10px] font-semibold text-[var(--accent)] hover:bg-[var(--accent)]/10"
                      title="Instancier sur le diagramme"
                    >
                      Place
                    </button>
                    {onSelectModuleForEdit && (
                      <button
                        type="button"
                        onClick={(e) => {
                          e.stopPropagation();
                          onSelectModuleForEdit(m.name);
                        }}
                        className="flex items-center gap-0.5 rounded px-1.5 py-0.5 text-[10px] font-semibold text-[var(--accent)] hover:bg-[var(--accent)]/10"
                        title="Éditer le modèle"
                      >
                        Edit <ArrowRight size={10} />
                      </button>
                    )}
                  </div>
                </div>
                {m.ports.length > 0 && (
                  <div className="mt-1.5 flex flex-wrap gap-1 pl-[22px]">
                    {m.ports.slice(0, 8).map((p) => (
                      <span
                        key={p}
                        className="mono rounded border border-[var(--accent)]/25 bg-[var(--accent)]/5 px-1.5 py-0.5 text-[9px] font-medium text-[var(--accent)]"
                      >
                        {p}
                      </span>
                    ))}
                    {m.ports.length > 8 && (
                      <span className="text-[9px] text-[var(--ink-faint)]">
                        +{m.ports.length - 8}
                      </span>
                    )}
                  </div>
                )}
              </div>
            );
          })}
        </div>
      )}
    </div>
  );
}

/* ── Diagram = nodes on the open canvas ───────────────────────────── */

function DiagramSection({
  open,
  onToggle,
  query,
  activeClassName,
  onEnterModule,
}: {
  open: boolean;
  onToggle: () => void;
  query: string;
  activeClassName?: string | null;
  onEnterModule?: (moduleName: string, instanceName: string) => void;
}) {
  const nodes = useDiagramStore((s) => s.nodes);
  const selectedNodeId = useDiagramStore((s) => s.selectedNodeId);
  const setSelected = useDiagramStore((s) => s.setSelected);
  const [expanded, setExpanded] = useState<Record<string, boolean>>({});

  const filtered = query
    ? nodes.filter((n) => {
        const data = n.data as unknown as EntropykNodeData;
        return (
          data.name.toLowerCase().includes(query) ||
          String(data.type).toLowerCase().includes(query) ||
          String(data.moduleName ?? "").toLowerCase().includes(query)
        );
      })
    : nodes;

  const sectionLabel = activeClassName ? `Diagram · ${activeClassName}` : "Diagram";

  return (
    <div className="flex flex-col">
      <SectionHeader
        open={open}
        onToggle={onToggle}
        icon={<Box size={12} className="text-[var(--ink-dim)]" />}
        label={sectionLabel}
        count={nodes.length}
      />
      {open && (
        <div className="py-1">
          {filtered.length === 0 ? (
            <p className="px-3 py-2 text-[10.5px] leading-snug text-[var(--ink-faint)]">
              {query
                ? "Aucun élément."
                : activeClassName
                  ? "Diagramme vide — dépose des parts pour définir ce modèle."
                  : "Rien sur le canvas — glisse un modèle depuis Models, ou des parts."}
            </p>
          ) : (
            filtered.map((n) => {
              const data = n.data as unknown as EntropykNodeData;
              const isModule = data.type === "ModuleInstance" || !!data.moduleName;
              const isSelected = n.id === selectedNodeId;
              const isExpanded = !!expanded[n.id];
              const label = isModule ? (
                <span className="flex items-center gap-1 truncate">
                  <span className="truncate">{data.name}</span>
                  <span className="mono text-[9px] text-[var(--ink-faint)]">
                    : {String(data.moduleName ?? data.params?.module_name ?? "")}
                  </span>
                </span>
              ) : (
                <span className="truncate">{data.name}</span>
              );

              return (
                <div key={n.id}>
                  <TreeRow
                    icon={
                      isModule ? (
                        <Box size={11} className="text-[var(--accent)]" />
                      ) : (
                        <Circle size={8} className="text-[var(--ink-faint)]" />
                      )
                    }
                    label={label}
                    depth={0}
                    active={isSelected}
                    onClick={() => setSelected(n.id)}
                    expandable={isModule}
                    expanded={isExpanded}
                    onToggle={() => setExpanded((e) => ({ ...e, [n.id]: !e[n.id] }))}
                  />
                  {isModule && isExpanded && (
                    <div className="ml-6 py-0.5">
                      <button
                        type="button"
                        onClick={(e) => {
                          e.stopPropagation();
                          const mName = String(
                            data.moduleName ?? data.params?.module_name ?? "",
                          );
                          if (mName && onEnterModule) onEnterModule(mName, data.name);
                        }}
                        className="flex items-center gap-1 rounded px-2 py-0.5 text-[10.5px] text-[var(--accent)] hover:bg-[var(--accent-subtle)]"
                      >
                        Ouvrir le modèle <ArrowRight size={10} />
                      </button>
                    </div>
                  )}
                </div>
              );
            })
          )}
        </div>
      )}
    </div>
  );
}

interface TreeRowProps {
  icon: React.ReactNode;
  label: React.ReactNode;
  depth: number;
  active?: boolean;
  onClick?: () => void;
  expandable?: boolean;
  expanded?: boolean;
  onToggle?: () => void;
}

function TreeRow({
  icon,
  label,
  depth,
  active,
  onClick,
  expandable,
  expanded,
  onToggle,
}: TreeRowProps) {
  return (
    <div
      onClick={onClick}
      className={`flex cursor-pointer items-center gap-1.5 rounded-[2px] py-1 pr-2 text-[11.5px] transition-colors ${
        active
          ? "bg-[var(--accent-subtle)] text-[var(--accent)]"
          : "text-[var(--ink-dim)] hover:bg-[var(--chrome-2)]"
      }`}
      style={{ paddingLeft: 8 + depth * 14 }}
    >
      {expandable ? (
        <button
          type="button"
          onClick={(e) => {
            e.stopPropagation();
            onToggle?.();
          }}
          className="text-[var(--ink-faint)] hover:text-[var(--ink)]"
        >
          {expanded ? <ChevronDown size={10} /> : <ChevronRight size={10} />}
        </button>
      ) : (
        <span className="w-[10px]" />
      )}
      <span className="flex h-4 w-4 items-center justify-center">{icon}</span>
      <span className="min-w-0 flex-1 truncate">{label}</span>
    </div>
  );
}

/* ── Parts (atomic thermo components) ─────────────────────────────── */

function PartsSection({
  open,
  onToggle,
  query,
}: {
  open: boolean;
  onToggle: () => void;
  query: string;
}) {
  const [collapsed, setCollapsed] = useState<Record<string, boolean>>({
    Advanced: true,
    Generic: true,
  });
  const toggle = (cat: string) => setCollapsed((c) => ({ ...c, [cat]: !c[cat] }));

  const onDragStart = (e: React.DragEvent, type: string) => {
    e.dataTransfer.setData("application/entropyk-type", type);
    e.dataTransfer.effectAllowed = "move";
  };

  const filteredCats = COMPONENT_CATEGORIES.map((cat) => ({
    cat,
    items: COMPONENTS.filter(
      (c) =>
        c.category === cat &&
        (!query ||
          c.label.toLowerCase().includes(query) ||
          c.type.toLowerCase().includes(query) ||
          (c.description ?? "").toLowerCase().includes(query)),
    ),
  })).filter((entry) => entry.items.length > 0);

  return (
    <div className="flex flex-col">
      <SectionHeader
        open={open}
        onToggle={onToggle}
        icon={<Boxes size={12} className="text-[var(--ink-dim)]" />}
        label="Parts"
        count={COMPONENTS.length}
      />
      {open && (
        <div className="py-1">
          {filteredCats.length === 0 && (
            <p className="px-3 py-2 text-[10.5px] text-[var(--ink-faint)]">
              Aucune part.
            </p>
          )}
          {filteredCats.map(({ cat, items }) => {
            const isOpen = !collapsed[cat] || !!query;
            return (
              <div key={cat} className="palette-cat">
                <button
                  type="button"
                  onClick={() => toggle(cat)}
                  aria-expanded={isOpen}
                  className="flex w-full items-center gap-1 px-2.5 py-1.5 text-left hover:bg-[var(--chrome-2)]"
                >
                  {isOpen ? (
                    <ChevronDown size={11} className="text-[var(--ink-faint)]" />
                  ) : (
                    <ChevronRight size={11} className="text-[var(--ink-faint)]" />
                  )}
                  <span className="eyebrow">{cat}</span>
                  <span className="mono ml-1 text-[9px] text-[var(--ink-faint)]">
                    {items.length}
                  </span>
                </button>
                <div className={`palette-cat-body ${isOpen ? "is-open" : "is-closed"}`}>
                  <div className="palette-cat-inner pb-1">
                    {items.map((c, i) => (
                      <PaletteItem key={c.type} meta={c} index={i} onDragStart={onDragStart} />
                    ))}
                  </div>
                </div>
              </div>
            );
          })}
        </div>
      )}
    </div>
  );
}

function PaletteItem({
  meta,
  index,
  onDragStart,
}: {
  meta: ComponentMeta;
  index: number;
  onDragStart: (e: React.DragEvent, type: string) => void;
}) {
  const itemType = String(meta.type || "");
  const family = hxFamily(itemType);
  const color = family?.accent ?? meta.color;

  return (
    <div
      draggable
      onDragStart={(e) => onDragStart(e, itemType)}
      className="palette-item group mx-1.5 flex cursor-grab items-center gap-2.5 rounded-[2px] px-2 py-1.5 hover:bg-[var(--chrome-2)] active:cursor-grabbing"
      style={{ ["--i" as string]: index }}
      title={meta.help ?? meta.description}
    >
      <span className="grid h-9 w-9 flex-shrink-0 place-items-center">
        <span style={{ width: 30, height: 30 }}>
          <ComponentIcon type={itemType} color={color} />
        </span>
      </span>
      <span className="min-w-0 flex-1">
        <span className="block truncate text-[12px] text-[var(--ink-dim)] group-hover:text-[var(--ink)]">
          {meta.label}
        </span>
        {family && (
          <span className="mono text-[8px] uppercase tracking-[0.08em] text-[var(--ink-faint)]">
            {family.badge} · {family.label}
          </span>
        )}
      </span>
    </div>
  );
}
