"use client";

import { useState, useCallback, useEffect, useMemo } from "react";
import dynamic from "next/dynamic";
import TopBar from "@/components/shell/TopBar";
import LeftNav from "@/components/shell/LeftNav";
import DocumentTabs from "@/components/shell/DocumentTabs";
import PropertiesPanel from "@/components/panels/PropertiesPanel";
import ResultsPanel from "@/components/panels/ResultsPanel";
import DofStatusBar from "@/components/DofStatusBar";
import NewModelModal, { type NewDocumentKind } from "@/components/modals/NewModelModal";
import { useDiagramStore } from "@/store/diagramStore";
import { useScenarioSimulation } from "@/lib/useScenarioSimulation";
import { useDocuments } from "@/lib/useDocuments";
import { fetchModule, saveModule } from "@/lib/api";

const Canvas = dynamic(() => import("@/components/canvas/Canvas"), { ssr: false });

export default function Page() {
  // Mount gate: avoid dead clicks from SSR/client hydration mismatches
  // (extensions, HMR, or browser tooling injecting attributes into HTML).
  const [mounted, setMounted] = useState(false);
  useEffect(() => {
    setMounted(true);
  }, []);

  if (!mounted) {
    return (
      <div className="flex h-screen items-center justify-center bg-[var(--chrome)] text-[13px] text-[var(--ink-dim)]">
        Chargement du workbench…
      </div>
    );
  }

  return <WorkbenchApp />;
}

function WorkbenchApp() {
  const { result, simError, lastConfig, nodes } = useDiagramStore();

  const { run: runSimulation, issues, simulating } = useScenarioSimulation();
  const {
    docs,
    activeDoc,
    activeId,
    workspaceName,
    switchTo,
    openModule,
    openEmptyModule,
    closeDoc,
    markClean,
    newScenario,
  } = useDocuments();

  const [newOpen, setNewOpen] = useState(false);
  const [newDefaultKind, setNewDefaultKind] = useState<NewDocumentKind>("class");
  const [saveError, setSaveError] = useState<string | null>(null);
  const editingModule = activeDoc.kind === "module" ? activeDoc.name : null;

  const scope = useMemo(() => {
    if (editingModule) return [workspaceName, editingModule];
    return [workspaceName];
  }, [workspaceName, editingModule]);

  const openNew = useCallback((kind: NewDocumentKind = "class") => {
    setNewDefaultKind(kind);
    setNewOpen(true);
  }, []);

  const handleSaveModuleEditor = useCallback(async () => {
    if (!editingModule) return;

    const state = useDiagramStore.getState();
    const currentNodes = state.nodes;
    const currentEdges = state.edges;

    const components: Array<Record<string, unknown>> = [];
    const instances: Array<{
      of: string;
      name: string;
      params?: Record<string, unknown>;
    }> = [];
    const atomicEdges: Array<{ from: string; to: string }> = [];
    const connections: Array<{ from: string; to: string }> = [];

    currentNodes.forEach((n) => {
      const data = n.data;
      if (data.type === "ModuleInstance" || data.moduleName) {
        instances.push({
          of: String(data.moduleName ?? data.params?.module_name ?? "BaseChiller"),
          name: data.name,
          params: data.params,
        });
      } else {
        components.push({
          type: data.kind,
          name: data.name,
          ...data.params,
        });
      }
    });

    currentEdges.forEach((e) => {
      const sNode = currentNodes.find((n) => n.id === e.source);
      const tNode = currentNodes.find((n) => n.id === e.target);
      if (!sNode || !tNode) return;

      const sData = sNode.data;
      const tData = tNode.data;
      const isInstanceEdge =
        sData.type === "ModuleInstance" || tData.type === "ModuleInstance";
      const fromPort = e.sourceHandle ?? "outlet";
      const toPort = e.targetHandle ?? "inlet";

      if (isInstanceEdge) {
        connections.push({
          from: `${sData.name}.${fromPort}`,
          to: `${tData.name}.${toPort}`,
        });
      } else {
        atomicEdges.push({
          from: `${sData.name}:${fromPort}`,
          to: `${tData.name}:${toPort}`,
        });
      }
    });

    // Preserve existing port map (component:port) when re-saving a class.
    let ports: Record<string, string> = {};
    try {
      const existing = await fetchModule(editingModule);
      if (existing && typeof existing === "object" && "ports" in existing) {
        const p = (existing as { ports?: Record<string, string> }).ports;
        if (p && typeof p === "object") ports = { ...p };
      }
    } catch {
      /* empty class or offline */
    }

    const res = await saveModule(editingModule, {
      params: {},
      components,
      instances,
      edges: atomicEdges,
      connections,
      ports,
    });

    if (res.ok) {
      if (typeof window !== "undefined") {
        window.dispatchEvent(new Event("entropyk-modules-updated"));
      }
      markClean(activeId);
      setSaveError(null);
    } else {
      setSaveError(`Could not save class: ${res.error ?? "unknown error"}`);
    }
  }, [editingModule, activeId, markClean]);

  useEffect(() => {
    (window as unknown as Record<string, unknown>).useDiagramStore = useDiagramStore;
  }, []);

  useEffect(() => {
    const onKey = (e: KeyboardEvent) => {
      if ((e.ctrlKey || e.metaKey) && (e.key === "Enter" || e.key === "NumpadEnter")) {
        e.preventDefault();
        void runSimulation();
      }
    };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [runSimulation]);

  // Clear any stale save error when the user switches documents.
  useEffect(() => {
    setSaveError(null);
  }, [activeId]);

  return (
    <div className="flex h-screen flex-col">
      {saveError && (
        <div className="flex items-center justify-between gap-2 rounded-md border border-[var(--warn)] bg-[var(--warn)]/10 px-3 py-2 text-[12px] text-[var(--warn)]">
          <span>{saveError}</span>
          <button type="button" className="toolish" aria-label="Fermer" onClick={() => setSaveError(null)}>✕</button>
        </div>
      )}
      <TopBar
        scope={scope}
        unsaved={activeDoc.dirty}
        editingModule={editingModule}
        workspaceName={workspaceName}
        onNavigateBack={() => switchTo("workspace")}
        onExitModuleEditor={() => closeDoc(activeId)}
        onSaveModuleEditor={handleSaveModuleEditor}
        onOpenNew={openNew}
        onSimulate={runSimulation}
        simulating={simulating}
      />

      <DocumentTabs docs={docs} activeId={activeId} onSwitch={switchTo} onClose={closeDoc} />

      <div className="flex min-h-0 flex-1 overflow-hidden">
        <LeftNav
          onSelectModuleForEdit={openModule}
          onEnterModule={(className) => void openModule(className)}
          onOpenNewModelModal={() => openNew("class")}
          activeClassName={editingModule}
        />

        <main className="relative flex-1">
          <Canvas onOpenModuleEditor={openModule} />
          {nodes.length === 0 && (
            <EmptyHint
              editingModule={editingModule}
              workspaceName={workspaceName}
              onNewClass={() => openNew("class")}
            />
          )}
        </main>

        <aside className="flex w-96 flex-col overflow-y-auto border-l border-[var(--line)] bg-[var(--panel)]">
          <section
            className="flex flex-col border-b border-[var(--line)]"
            aria-label="Component inspector"
          >
            <div className="flex h-7 items-center justify-between bg-[var(--chrome)] px-3">
              <span className="eyebrow">Inspector</span>
              <span className="mono text-[10px] text-[var(--ink-faint)]">
                {editingModule ? `${editingModule}.ekmod` : workspaceName}
              </span>
            </div>
            <PropertiesPanel />
          </section>

          <section className="flex min-h-0 flex-1 flex-col" aria-label="Solve bench">
            <ResultsPanel
              result={result}
              error={simError}
              config={lastConfig}
              simulating={simulating}
              issues={issues}
            />
          </section>
        </aside>
      </div>

      <DofStatusBar />

      <NewModelModal
        isOpen={newOpen}
        onClose={() => setNewOpen(false)}
        defaultKind={newDefaultKind}
        onScenarioCreated={(name, clearCanvas) => newScenario(name, clearCanvas)}
        onClassCreated={(className) => {
          openEmptyModule(className);
          setNewOpen(false);
        }}
      />
    </div>
  );
}

function EmptyHint({
  editingModule,
  workspaceName,
  onNewClass,
}: {
  editingModule?: string | null;
  workspaceName: string;
  onNewClass: () => void;
}) {
  const wsLabel =
    workspaceName === "Untitled" || !workspaceName ? "Workspace" : workspaceName;

  return (
    <div className="pointer-events-none absolute inset-0 grid place-items-center">
      <div className="max-w-md px-6 text-center">
        {editingModule ? (
          <>
            <p className="mono text-[15px] font-semibold text-[var(--ink)]">
              {editingModule}
              <span className="text-[var(--ink-faint)]">.ekmod</span>
            </p>
            <p className="mt-1.5 text-[12px] leading-relaxed text-[var(--ink-dim)]">
              Édition de la classe — dépose des parts, câble, puis Save. Ensuite
              retourne au Workspace pour l’instancier.
            </p>
          </>
        ) : (
          <>
            <p className="text-[15px] font-semibold text-[var(--ink)]">{wsLabel}</p>
            <p className="mt-1.5 text-[12px] leading-relaxed text-[var(--ink-dim)]">
              Canvas Workspace — glisse un modèle depuis Models (à gauche), ou crée-en un
              nouveau. Tes .ekmod sont des classes Modelica.
            </p>
            <button
              type="button"
              onClick={onNewClass}
              className="pointer-events-auto mt-3 rounded-md bg-[var(--accent)] px-3 py-1.5 text-[12px] font-semibold text-white hover:bg-[var(--accent-hover,#1d4ed8)]"
            >
              Nouveau modèle
            </button>
          </>
        )}
      </div>
    </div>
  );
}
