import React, { useState, useCallback, useMemo } from "react";
import { createRoot } from "react-dom/client";
import { useApp, useHostStyles } from "@modelcontextprotocol/ext-apps/react";

// ─── Types ─────────────────────────────────────────────
interface NodeTemplate {
  name: string;
  type: string;
  icon: string;
  category: string;
  defaultParams: Record<string, string>;
}

interface CanvasNode {
  id: string;
  name: string;
  type: string;
  icon: string;
  category: string;
  parameters: Record<string, string>;
}

interface ToastState {
  message: string;
  type: "success" | "error";
}

// ─── Node Palette Data ─────────────────────────────────
const NODE_CATEGORIES: {
  name: string;
  icon: string;
  color: string;
  nodes: NodeTemplate[];
}[] = [
  {
    name: "Triggers",
    icon: "⚡",
    color: "#ff6d5a",
    nodes: [
      { name: "Webhook", type: "n8n-nodes-base.webhook", icon: "🌐", category: "Triggers", defaultParams: { httpMethod: "POST", path: "/webhook" } },
      { name: "Schedule", type: "n8n-nodes-base.scheduleTrigger", icon: "⏰", category: "Triggers", defaultParams: { rule: "0 * * * *", cronExpression: "" } },
      { name: "Chat", type: "@n8n/n8n-nodes-langchain.chatTrigger", icon: "💬", category: "Triggers", defaultParams: { mode: "webhook", allowedOrigins: "*" } },
      { name: "Form", type: "n8n-nodes-base.formTrigger", icon: "📝", category: "Triggers", defaultParams: { formTitle: "My Form", formDescription: "" } },
      { name: "Manual", type: "n8n-nodes-base.manualTrigger", icon: "👆", category: "Triggers", defaultParams: {} },
    ],
  },
  {
    name: "Actions",
    icon: "🔧",
    color: "#58a6ff",
    nodes: [
      { name: "HTTP Request", type: "n8n-nodes-base.httpRequest", icon: "🌍", category: "Actions", defaultParams: { method: "GET", url: "https://", responseFormat: "json" } },
      { name: "Slack", type: "n8n-nodes-base.slack", icon: "💬", category: "Actions", defaultParams: { resource: "message", operation: "send", channel: "" } },
      { name: "Gmail", type: "n8n-nodes-base.gmail", icon: "📧", category: "Actions", defaultParams: { resource: "message", operation: "send", to: "" } },
      { name: "Discord", type: "n8n-nodes-base.discord", icon: "🎮", category: "Actions", defaultParams: { resource: "message", operation: "send", webhookUrl: "" } },
      { name: "Telegram", type: "n8n-nodes-base.telegram", icon: "✈️", category: "Actions", defaultParams: { resource: "message", operation: "sendMessage", chatId: "" } },
    ],
  },
  {
    name: "Transform",
    icon: "🔄",
    color: "#d29922",
    nodes: [
      { name: "Code", type: "n8n-nodes-base.code", icon: "💻", category: "Transform", defaultParams: { language: "javaScript", jsCode: "return items;" } },
      { name: "Set", type: "n8n-nodes-base.set", icon: "✏️", category: "Transform", defaultParams: { mode: "manual", assignments: "" } },
      { name: "IF", type: "n8n-nodes-base.if", icon: "🔀", category: "Transform", defaultParams: { conditions: "", combineOperation: "all" } },
      { name: "Switch", type: "n8n-nodes-base.switch", icon: "🔃", category: "Transform", defaultParams: { mode: "rules", rules: "" } },
      { name: "Merge", type: "n8n-nodes-base.merge", icon: "🔗", category: "Transform", defaultParams: { mode: "append" } },
    ],
  },
  {
    name: "Flow",
    icon: "🔁",
    color: "#3fb950",
    nodes: [
      { name: "Wait", type: "n8n-nodes-base.wait", icon: "⏳", category: "Flow", defaultParams: { amount: "1", unit: "seconds" } },
      { name: "Loop", type: "n8n-nodes-base.splitInBatches", icon: "🔁", category: "Flow", defaultParams: { batchSize: "10" } },
      { name: "Split In Batches", type: "n8n-nodes-base.splitInBatches", icon: "📦", category: "Flow", defaultParams: { batchSize: "10", options: "" } },
      { name: "Error Trigger", type: "n8n-nodes-base.errorTrigger", icon: "🚨", category: "Flow", defaultParams: {} },
    ],
  },
  {
    name: "AI",
    icon: "🤖",
    color: "#a371f7",
    nodes: [
      { name: "OpenAI", type: "@n8n/n8n-nodes-langchain.openAi", icon: "🧠", category: "AI", defaultParams: { resource: "chat", operation: "complete", model: "gpt-4" } },
      { name: "AI Agent", type: "@n8n/n8n-nodes-langchain.agent", icon: "🤖", category: "AI", defaultParams: { agentType: "conversational", systemMessage: "" } },
      { name: "Vector Store", type: "@n8n/n8n-nodes-langchain.vectorStore", icon: "📊", category: "AI", defaultParams: { mode: "retrieve", topK: "4" } },
      { name: "Text Classifier", type: "@n8n/n8n-nodes-langchain.textClassifier", icon: "🏷️", category: "AI", defaultParams: { categories: "", inputText: "" } },
    ],
  },
];

let nodeIdCounter = 0;
function genNodeId(): string {
  return `node_${++nodeIdCounter}_${Date.now()}`;
}

function isTriggerCategory(category: string): boolean {
  return category === "Triggers";
}

// ─── Toast Component ───────────────────────────────────
function Toast({ toast, onDismiss }: { toast: ToastState; onDismiss: () => void }) {
  React.useEffect(() => {
    const timer = setTimeout(onDismiss, 4000);
    return () => clearTimeout(timer);
  }, [onDismiss]);

  return (
    <div className={`toast ${toast.type}`} onClick={onDismiss}>
      {toast.type === "success" ? "✓" : "✗"} {toast.message}
    </div>
  );
}

// ─── Palette Component ─────────────────────────────────
function NodePalette({ onAddNode }: { onAddNode: (template: NodeTemplate) => void }) {
  const [openCategories, setOpenCategories] = useState<Set<string>>(
    new Set(NODE_CATEGORIES.map((c) => c.name))
  );

  const toggleCategory = (name: string) => {
    setOpenCategories((prev) => {
      const next = new Set(prev);
      if (next.has(name)) next.delete(name);
      else next.add(name);
      return next;
    });
  };

  return (
    <div className="builder-sidebar">
      <div className="palette-header">🧩 Node Palette</div>
      {NODE_CATEGORIES.map((cat) => (
        <div className="palette-category" key={cat.name}>
          <div className="palette-category-header" onClick={() => toggleCategory(cat.name)}>
            <span className={`chevron ${openCategories.has(cat.name) ? "open" : ""}`}>▶</span>
            <span className="cat-icon">{cat.icon}</span>
            <span className="cat-name">{cat.name}</span>
            <span className="cat-count">{cat.nodes.length}</span>
          </div>
          {openCategories.has(cat.name) && (
            <div className="palette-nodes">
              {cat.nodes.map((node) => (
                <div
                  key={node.type + node.name}
                  className="palette-node"
                  onClick={() => onAddNode(node)}
                  title={`Add ${node.name} node`}
                >
                  <span className="node-icon">{node.icon}</span>
                  <span>{node.name}</span>
                </div>
              ))}
            </div>
          )}
        </div>
      ))}
    </div>
  );
}

// ─── Canvas Component ──────────────────────────────────
function BuilderCanvas({
  nodes,
  selectedId,
  onSelect,
  onRemove,
}: {
  nodes: CanvasNode[];
  selectedId: string | null;
  onSelect: (id: string) => void;
  onRemove: (id: string) => void;
}) {
  if (nodes.length === 0) {
    return (
      <div className="builder-canvas">
        <div className="canvas-grid" />
        <div className="empty-state" style={{ position: "relative", zIndex: 2 }}>
          <div className="icon">🔨</div>
          <h3>Start Building</h3>
          <p>Click nodes in the palette to add them to your workflow. They'll auto-connect in sequence.</p>
        </div>
      </div>
    );
  }

  return (
    <div className="builder-canvas">
      <div className="canvas-grid" />
      <div className="canvas-flow">
        {nodes.map((node, idx) => (
          <div className="canvas-node-wrapper" key={node.id}>
            {idx > 0 && <div className="canvas-connector" />}
            <div
              className={`canvas-node ${selectedId === node.id ? "selected" : ""} ${
                isTriggerCategory(node.category) ? "trigger-node" : ""
              }`}
              onClick={() => onSelect(node.id)}
            >
              <span className="node-icon">{node.icon}</span>
              <div className="node-info">
                <div className="node-name">{node.name}</div>
                <div className="node-type">{node.category}</div>
              </div>
              <button
                className="node-remove"
                onClick={(e) => {
                  e.stopPropagation();
                  onRemove(node.id);
                }}
                title="Remove node"
              >
                ✕
              </button>
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

// ─── Config Panel Component ────────────────────────────
function ConfigPanel({
  node,
  onUpdate,
}: {
  node: CanvasNode | null;
  onUpdate: (id: string, field: string, value: string) => void;
}) {
  const [newParamKey, setNewParamKey] = useState("");

  if (!node) {
    return (
      <div className="builder-panel">
        <div className="panel-header">Node Configuration</div>
        <div className="panel-empty">
          <span className="icon">⚙️</span>
          <span>Select a node to configure</span>
        </div>
      </div>
    );
  }

  const addParam = () => {
    const key = newParamKey.trim();
    if (key && !node.parameters[key]) {
      onUpdate(node.id, key, "");
      setNewParamKey("");
    }
  };

  return (
    <div className="builder-panel">
      <div className="panel-header">
        <span>{node.icon} {node.name}</span>
        <span className="badge n8n">{node.category}</span>
      </div>
      <div className="panel-body">
        {/* Name field */}
        <div className="panel-field">
          <label>Node Name</label>
          <input
            className="input"
            type="text"
            value={node.name}
            onChange={(e) => onUpdate(node.id, "__name__", e.target.value)}
          />
        </div>

        {/* Type (read-only) */}
        <div className="panel-field">
          <label>Node Type</label>
          <input className="input" type="text" value={node.type} readOnly style={{ opacity: 0.6 }} />
        </div>

        {/* Parameters */}
        <div className="panel-field">
          <label>Parameters</label>
          {Object.entries(node.parameters).map(([key, value]) => (
            <div className="param-row" key={key}>
              <div className="param-key">{key}</div>
              <input
                className="input"
                type="text"
                value={value}
                onChange={(e) => onUpdate(node.id, key, e.target.value)}
                placeholder={`Enter ${key}...`}
              />
            </div>
          ))}
          {Object.keys(node.parameters).length === 0 && (
            <div style={{ fontSize: 12, color: "var(--color-text-dim)", padding: "8px 0" }}>
              No parameters for this node type
            </div>
          )}
        </div>

        {/* Add Parameter */}
        <div className="panel-field">
          <label>Add Parameter</label>
          <div style={{ display: "flex", gap: 8 }}>
            <input
              className="input"
              type="text"
              placeholder="Parameter name..."
              value={newParamKey}
              onChange={(e) => setNewParamKey(e.target.value)}
              onKeyDown={(e) => e.key === "Enter" && addParam()}
              style={{ flex: 1 }}
            />
            <button
              className="btn btn-sm btn-secondary"
              onClick={addParam}
              disabled={!newParamKey.trim()}
            >
              + Add
            </button>
          </div>
        </div>
      </div>
    </div>
  );
}

// ─── Main Builder Component ────────────────────────────
function WorkflowBuilder() {
  const [workflowName, setWorkflowName] = useState("New Workflow");
  const [nodes, setNodes] = useState<CanvasNode[]>([]);
  const [selectedNodeId, setSelectedNodeId] = useState<string | null>(null);
  const [toast, setToast] = useState<ToastState | null>(null);
  const [saving, setSaving] = useState(false);
  const [zoom, setZoom] = useState(100);
  const [connectionMode, setConnectionMode] = useState<"sequential" | "manual">("sequential");
  const [serverToolsAvailable, setServerToolsAvailable] = useState(false);

  const { app, isConnected, error } = useApp({
    appInfo: { name: "n8n-workflow-builder", version: "1.0.0" },
    capabilities: {},
    onAppCreated: (app) => {
      app.ontoolresult = (params: any) => {
        // Handle initial data from the tool invocation
        try {
          const textContent = params.content?.find(
            (c: { type: string }) => c.type === "text"
          ) as { type: "text"; text: string } | undefined;
          if (textContent?.text) {
            const parsed = JSON.parse(textContent.text);
            if (parsed?.name) setWorkflowName(parsed.name);
          }
        } catch {
          // ignore parse errors
        }
      };

      app.ontoolinput = () => {
        // tool input received
      };
    },
  });

  useHostStyles(app, app?.getHostContext());

  // Check capabilities once connected
  React.useEffect(() => {
    if (app && isConnected) {
      try {
        const caps = app.getHostCapabilities();
        setServerToolsAvailable(!!caps?.serverTools);
      } catch {
        setServerToolsAvailable(false);
      }
    }
  }, [app, isConnected]);

  // Poll for workflow ops from the parent app (plain language → workflow builder)
  React.useEffect(() => {
    let active = true;

    const processOps = (ops: {
      name: string;
      nodes: {
        name: string;
        type: string;
        icon: string;
        category: string;
        parameters: Record<string, string>;
      }[];
    }) => {
      // Set workflow name
      setWorkflowName(ops.name || "AI-Built Workflow");

      // Clear existing nodes first
      setNodes([]);
      setSelectedNodeId(null);

      // Animate adding nodes one by one with staggered delay
      const nodeCount = ops.nodes.length;
      ops.nodes.forEach((nodeOps, index) => {
        setTimeout(() => {
          if (!active) return;
          const newNode: CanvasNode = {
            id: genNodeId(),
            name: nodeOps.name,
            type: nodeOps.type,
            icon: nodeOps.icon || "⚡",
            category: nodeOps.category || "Actions",
            parameters: nodeOps.parameters || {},
          };
          setNodes((prev) => [...prev, newNode]);
          setSelectedNodeId(newNode.id);

          // Show toast after last node is added
          if (index === nodeCount - 1) {
            setToast({
              message: `✨ Built ${nodeCount} nodes from your description`,
              type: "success",
            });
          }
        }, (index + 1) * 300);
      });
    };

    // Poll the workflow-ops API endpoint
    const poll = async () => {
      while (active) {
        try {
          const res = await fetch("/api/workflow-ops");
          if (res.ok) {
            const data = await res.json();
            if (data.ops) {
              processOps(data.ops);
            }
          }
        } catch {
          // ignore fetch errors
        }
        // Wait 500ms between polls
        await new Promise((r) => setTimeout(r, 500));
      }
    };

    poll();

    // Also still listen for postMessage as backup
    const handler = (event: MessageEvent) => {
      if (event.data?.type === "workflow_ops" && event.data.payload) {
        processOps(event.data.payload);
      }
    };
    window.addEventListener("message", handler);

    return () => {
      active = false;
      window.removeEventListener("message", handler);
    };
  }, []);

  // Selected node
  const selectedNode = useMemo(
    () => nodes.find((n) => n.id === selectedNodeId) || null,
    [nodes, selectedNodeId]
  );

  // Handlers
  const showToast = useCallback((message: string, type: "success" | "error") => {
    setToast({ message, type });
  }, []);

  const handleAddNode = useCallback((template: NodeTemplate) => {
    const newNode: CanvasNode = {
      id: genNodeId(),
      name: template.name,
      type: template.type,
      icon: template.icon,
      category: template.category,
      parameters: { ...template.defaultParams },
    };
    setNodes((prev) => [...prev, newNode]);
    setSelectedNodeId(newNode.id);
  }, []);

  const handleRemoveNode = useCallback(
    (id: string) => {
      setNodes((prev) => prev.filter((n) => n.id !== id));
      if (selectedNodeId === id) setSelectedNodeId(null);
    },
    [selectedNodeId]
  );

  const handleUpdateNode = useCallback((id: string, field: string, value: string) => {
    setNodes((prev) =>
      prev.map((n) => {
        if (n.id !== id) return n;
        if (field === "__name__") return { ...n, name: value };
        return { ...n, parameters: { ...n.parameters, [field]: value } };
      })
    );
  }, []);

  const handleClearAll = useCallback(() => {
    setNodes([]);
    setSelectedNodeId(null);
  }, []);

  const handleSave = useCallback(async () => {
    if (!app || nodes.length === 0) {
      showToast("Add at least one node before saving", "error");
      return;
    }
    if (!serverToolsAvailable) {
      showToast("Server tools not available — cannot save", "error");
      return;
    }

    setSaving(true);

    // Build n8n-compatible nodes
    const n8nNodes = nodes.map((node, idx) => ({
      id: node.id,
      name: node.name,
      type: node.type,
      typeVersion: 1,
      position: [250, 100 + idx * 200],
      parameters: Object.fromEntries(
        Object.entries(node.parameters).filter(([_, v]) => v !== "")
      ),
    }));

    // Build sequential connections
    const connections: Record<string, { main: Array<Array<{ node: string; type: string; index: number }>> }> = {};
    for (let i = 0; i < nodes.length - 1; i++) {
      connections[nodes[i].name] = {
        main: [[{ node: nodes[i + 1].name, type: "main", index: 0 }]],
      };
    }

    try {
      const result = await app.callServerTool({
        name: "n8n_create_workflow_action",
        arguments: {
          name: workflowName,
          nodes: JSON.stringify(n8nNodes),
          connections: JSON.stringify(connections),
        },
      });

      if (!result.isError) {
        // Try to extract workflow ID from result
        try {
          const textContent = result.content?.find(
            (c: { type: string }) => c.type === "text"
          ) as { type: "text"; text: string } | undefined;
          if (textContent?.text) {
            const parsed = JSON.parse(textContent.text);
            if (parsed?.id) {
              showToast(`Workflow saved! ID: ${parsed.id}`, "success");
            } else {
              showToast("Workflow saved successfully", "success");
            }
          } else {
            showToast("Workflow saved successfully", "success");
          }
        } catch {
          showToast("Workflow saved successfully", "success");
        }
      } else {
        showToast("Save failed", "error");
      }
    } catch (err: any) {
      showToast(err.message || "Save failed", "error");
    }

    setSaving(false);
  }, [app, nodes, workflowName, serverToolsAvailable, showToast]);

  // ─── Render ────────────────────────────────────────
  if (error) {
    return (
      <div style={{ display: "flex", alignItems: "center", justifyContent: "center", height: "100vh", color: "var(--color-text-muted)" }}>
        <span>⚠️ Connection error: {error.message}</span>
      </div>
    );
  }

  return (
    <div className="builder-layout">
      {/* Top Bar */}
      <div className="builder-topbar">
        <span style={{ fontSize: 20 }}>⚡</span>
        <input
          className="workflow-name-input"
          type="text"
          value={workflowName}
          onChange={(e) => setWorkflowName(e.target.value)}
          placeholder="Workflow name..."
        />
        <div className="topbar-meta">
          <span className="node-count-badge">🔧 {nodes.length} nodes</span>
          <button
            className="btn btn-primary btn-sm"
            onClick={handleSave}
            disabled={saving || nodes.length === 0}
          >
            {saving ? (
              <>
                <span className="spinner" style={{ width: 14, height: 14 }} /> Saving...
              </>
            ) : (
              "💾 Save Workflow"
            )}
          </button>
        </div>
      </div>

      {/* Left Sidebar — Node Palette */}
      <NodePalette onAddNode={handleAddNode} />

      {/* Main Canvas */}
      <BuilderCanvas
        nodes={nodes}
        selectedId={selectedNodeId}
        onSelect={setSelectedNodeId}
        onRemove={handleRemoveNode}
      />

      {/* Right Panel — Config */}
      <ConfigPanel node={selectedNode} onUpdate={handleUpdateNode} />

      {/* Bottom Bar */}
      <div className="builder-bottombar">
        <div style={{ display: "flex", alignItems: "center", gap: 16 }}>
          <button
            className={`btn btn-sm ${connectionMode === "sequential" ? "btn-primary" : "btn-ghost"}`}
            onClick={() =>
              setConnectionMode((m) => (m === "sequential" ? "manual" : "sequential"))
            }
            title="Toggle connection mode"
          >
            {connectionMode === "sequential" ? "🔗 Sequential" : "📌 Manual"}
          </button>
          <button
            className="btn btn-danger btn-sm"
            onClick={handleClearAll}
            disabled={nodes.length === 0}
          >
            🗑️ Clear All
          </button>
        </div>

        <div className="zoom-controls">
          <button className="zoom-btn" onClick={() => setZoom((z) => Math.max(50, z - 10))}>−</button>
          <span className="zoom-label">{zoom}%</span>
          <button className="zoom-btn" onClick={() => setZoom((z) => Math.min(200, z + 10))}>+</button>
          <button className="zoom-btn" onClick={() => setZoom(100)} title="Reset zoom">⊙</button>
        </div>

        <div style={{ fontSize: 11, color: "var(--color-text-dim)" }}>
          n8n Workflow Builder v1.0
        </div>
      </div>

      {/* Toast */}
      {toast && <Toast toast={toast} onDismiss={() => setToast(null)} />}
    </div>
  );
}

// ─── Mount ─────────────────────────────────────────────
const root = createRoot(document.getElementById("root")!);
root.render(<WorkflowBuilder />);
