"use client";

import { useEffect, useState } from "react";
import { X, PackagePlus, Plus, Trash2, CheckCircle2, AlertCircle } from "lucide-react";
import { useDiagramStore, type EntropykNodeData } from "@/store/diagramStore";
import { COMPONENT_BY_TYPE } from "@/lib/componentMeta";
import { saveModule } from "@/lib/api";
import type { Node } from "@xyflow/react";

interface CreateModuleModalProps {
  isOpen: boolean;
  onClose: () => void;
  onSuccess?: () => void;
}

interface ExposedPortItem {
  id: string;
  exposedName: string;
  compPortRef: string; // e.g. "evap:secondary_inlet"
}

export default function CreateModuleModal({
  isOpen,
  onClose,
  onSuccess,
}: CreateModuleModalProps) {
  const { nodes, edges } = useDiagramStore();
  const [moduleName, setModuleName] = useState("");
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [success, setSuccess] = useState(false);

  // Collect all available component:port references from current canvas nodes
  const availableComponentPorts: Array<{ label: string; ref: string }> = [];
  nodes.forEach((n) => {
    const data = n.data as unknown as EntropykNodeData;
    if (data.type === "ModuleInstance") return; // Skip nested module instances for flat component extraction
    const meta = COMPONENT_BY_TYPE[data.type];
    const ports = meta?.ports ?? ["inlet", "outlet"];
    ports.forEach((p) => {
      availableComponentPorts.push({
        label: `${data.name} (${p})`,
        ref: `${data.name}:${p}`,
      });
    });
  });

  // Default exposed ports proposal (auto-detect secondary / boundary ports)
  const [exposedPorts, setExposedPorts] = useState<ExposedPortItem[]>(() => {
    const items: ExposedPortItem[] = [];
    nodes.forEach((n) => {
      const data = n.data as unknown as EntropykNodeData;
      if (data.type === "Evaporator") {
        items.push({ id: `e1`, exposedName: "chw_in", compPortRef: `${data.name}:secondary_inlet` });
        items.push({ id: `e2`, exposedName: "chw_out", compPortRef: `${data.name}:secondary_outlet` });
      } else if (data.type === "Condenser") {
        items.push({ id: `c1`, exposedName: "cond_water_in", compPortRef: `${data.name}:secondary_inlet` });
        items.push({ id: `c2`, exposedName: "cond_water_out", compPortRef: `${data.name}:secondary_outlet` });
      }
    });
    if (items.length === 0 && availableComponentPorts.length > 0) {
      items.push({
        id: "p1",
        exposedName: "inlet",
        compPortRef: availableComponentPorts[0].ref,
      });
    }
    return items;
  });

  useEffect(() => {
    const onKey = (e: KeyboardEvent) => {
      if (e.key === "Escape") onClose();
    };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [onClose]);

  if (!isOpen) return null;

  const addExposedPort = () => {
    const defaultRef = availableComponentPorts[0]?.ref ?? "";
    setExposedPorts((prev) => [
      ...prev,
      {
        id: `port_${Date.now()}`,
        exposedName: `port_${prev.length + 1}`,
        compPortRef: defaultRef,
      },
    ]);
  };

  const removeExposedPort = (id: string) => {
    setExposedPorts((prev) => prev.filter((p) => p.id !== id));
  };

  const updateExposedPort = (id: string, field: "exposedName" | "compPortRef", val: string) => {
    setExposedPorts((prev) =>
      prev.map((p) => (p.id === id ? { ...p, [field]: val } : p)),
    );
  };

  const handleSave = async () => {
    const cleanName = moduleName.trim().replace(/[^a-zA-Z0-9_-]/g, "");
    if (!cleanName) {
      setError("Veuillez saisir un nom de module valide (alphanumérique).");
      return;
    }

    if (nodes.length === 0) {
      setError("Le canvas ne contient aucun composant ni module à inclure.");
      return;
    }

    setError(null);
    setLoading(true);

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

    nodes.forEach((n) => {
      const d = n.data as unknown as EntropykNodeData;
      if (d.type === "ModuleInstance") {
        instances.push({
          of: String(d.params.module_name ?? "BaseChiller"),
          name: d.name,
          circuit: d.circuit,
          params: {},
        });
      } else {
        const { type, name, ...params } = d;
        components.push({
          type: d.type,
          name: d.name,
          ...params,
        });
      }
    });

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

      const sData = sNode.data as unknown as EntropykNodeData;
      const tData = tNode.data as unknown as EntropykNodeData;

      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}` });
      }
    });

    const portsMap: Record<string, string> = {};
    exposedPorts.forEach((p) => {
      if (p.exposedName.trim() && p.compPortRef) {
        portsMap[p.exposedName.trim()] = p.compPortRef;
      }
    });

    const ekmodContent = {
      params: {},
      components,
      instances,
      edges: atomicEdges,
      connections,
      ports: portsMap,
    };

    const res = await saveModule(cleanName, ekmodContent);
    setLoading(false);

    if (res.ok) {
      setSuccess(true);
      if (typeof window !== "undefined") {
        window.dispatchEvent(new Event("entropyk-modules-updated"));
      }
      setTimeout(() => {
        setSuccess(false);
        onSuccess?.();
        onClose();
      }, 300);
    } else {
      setError(res.error || "Erreur lors de la création du module.");
    }
  };

  return (
    <div
      className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-xs p-4 overscroll-contain"
      role="dialog"
      aria-modal="true"
      aria-label="Créer un module"
    >
      <div className="flex w-full max-w-lg flex-col rounded-lg border border-[var(--line)] bg-white shadow-xl">
        {/* Header */}
        <div className="flex items-center justify-between border-b border-[var(--line)] px-4 py-3 bg-[var(--chrome)]">
          <div className="flex items-center gap-2">
            <PackagePlus size={18} className="text-[var(--accent)]" />
            <h3 className="mono text-[13px] font-bold text-[var(--ink)]">
              Save as class (.ekmod)
            </h3>
          </div>
          <button
            type="button"
            onClick={onClose}
            className="rounded p-1 text-[var(--ink-faint)] hover:bg-[var(--chrome-2)] hover:text-[var(--ink)]"
          >
            <X size={16} />
          </button>
        </div>

        {/* Body */}
        <div className="p-4 space-y-4 max-h-[75vh] overflow-y-auto">
          {error && (
            <div className="flex items-center gap-2 rounded border border-red-200 bg-red-50 p-2.5 text-[11px] text-red-700">
              <AlertCircle size={15} className="shrink-0 text-red-500" />
              <span>{error}</span>
            </div>
          )}

          {success && (
            <div className="flex items-center gap-2 rounded border border-emerald-200 bg-emerald-50 p-2.5 text-[11px] text-emerald-700">
              <CheckCircle2 size={15} className="shrink-0 text-emerald-500" />
              <span>Module sauvegardé avec succès dans `.entropyk/modules/` !</span>
            </div>
          )}

          {/* Module Name */}
          <div>
            <label className="block text-[11px] font-semibold text-[var(--ink-dim)] mb-1">
              Class name
            </label>
            <input
              type="text"
              value={moduleName}
              onChange={(e) => setModuleName(e.target.value)}
              placeholder="e.g. DualChillerCircuit"
              className="w-full rounded border border-[var(--line)] px-3 py-1.5 mono text-[12px] text-[var(--ink)] outline-none focus:border-[var(--accent)]"
            />
            <p className="mt-1 mono text-[10px] text-[var(--ink-faint)]">
              .entropyk/modules/{moduleName || "Name"}.ekmod
            </p>
          </div>

          {/* Exposed Ports Selector */}
          <div>
            <div className="flex items-center justify-between mb-1.5">
              <label className="text-[11px] font-semibold text-[var(--ink-dim)]">
                Ports exposés (Handles externes)
              </label>
              <button
                type="button"
                onClick={addExposedPort}
                className="flex items-center gap-1 rounded bg-[var(--accent)]/10 px-2 py-0.5 text-[10px] font-semibold text-[var(--accent)] hover:bg-[var(--accent)]/20"
              >
                <Plus size={11} /> Ajouter un port
              </button>
            </div>

            {exposedPorts.length === 0 ? (
              <p className="text-[10px] text-[var(--ink-faint)] italic py-1">
                Aucun port exposé sélectionné. Cliquez sur &quot;Ajouter un port&quot;.
              </p>
            ) : (
              <div className="space-y-2 border border-[var(--line)] rounded p-2 bg-[var(--chrome)]/30">
                {exposedPorts.map((item) => (
                  <div key={item.id} className="flex items-center gap-2">
                    <input
                      type="text"
                      value={item.exposedName}
                      onChange={(e) => updateExposedPort(item.id, "exposedName", e.target.value)}
                      placeholder="Nom exposé (ex: chw_in)"
                      className="w-1/3 rounded border border-[var(--line)] px-2 py-1 mono text-[10.5px] outline-none focus:border-[var(--accent)]"
                    />
                    <span className="text-[11px] text-[var(--ink-faint)]">→</span>
                    <select
                      value={item.compPortRef}
                      onChange={(e) => updateExposedPort(item.id, "compPortRef", e.target.value)}
                      className="flex-1 rounded border border-[var(--line)] px-2 py-1 mono text-[10.5px] outline-none focus:border-[var(--accent)]"
                    >
                      {availableComponentPorts.map((cp) => (
                        <option key={cp.ref} value={cp.ref}>
                          {cp.label}
                        </option>
                      ))}
                    </select>
                    <button
                      type="button"
                      onClick={() => removeExposedPort(item.id)}
                      className="rounded p-1 text-[var(--ink-faint)] hover:text-red-600 hover:bg-red-50"
                    >
                      <Trash2 size={12} />
                    </button>
                  </div>
                ))}
              </div>
            )}
          </div>
        </div>

        {/* Footer */}
        <div className="flex items-center justify-end gap-2 border-t border-[var(--line)] px-4 py-3 bg-[var(--chrome)]">
          <button
            type="button"
            onClick={onClose}
            className="rounded border border-[var(--line)] px-3 py-1.5 text-[11px] font-medium text-[var(--ink-dim)] hover:bg-[var(--chrome-2)]"
          >
            Annuler
          </button>
          <button
            type="button"
            onClick={handleSave}
            disabled={loading || success}
            className="flex items-center gap-1.5 rounded bg-[var(--accent)] px-3 py-1.5 text-[11px] font-semibold text-white shadow-sm hover:opacity-90 active:scale-95 disabled:opacity-50"
          >
            <PackagePlus size={14} />
            <span>{loading ? "…" : "Valider"}</span>
          </button>
        </div>
      </div>
    </div>
  );
}
