import React, { useEffect, useMemo, useState } from "react";

import { ApiClient } from "./api-client.ts";
import {
  addNodeToDraft,
  createDefaultWorkflowDraft,
  removeNodeFromDraft,
  resolveDefinitionIdForNode,
  serializeWorkflowDraft,
  workflowDraftFromVersion,
  type WorkflowDraft,
} from "./workflow-editor-state.ts";

type NavItem = "Assets" | "Workflows" | "Runs" | "Explore" | "Labels" | "Admin";

type BootstrapContext = {
  userId: string;
  workspace: { _id: string; name: string };
  project: { _id: string; name: string };
};

type AppProps = {
  apiBaseUrl: string;
};

function usePathname() {
  const [pathname, setPathname] = useState(
    typeof window === "undefined" ? "/assets" : window.location.pathname || "/assets",
  );

  useEffect(() => {
    const handle = () => setPathname(window.location.pathname || "/assets");
    window.addEventListener("popstate", handle);
    return () => window.removeEventListener("popstate", handle);
  }, []);

  return pathname === "/" ? "/assets" : pathname;
}

function AppShell(props: {
  workspaceName: string;
  projectName: string;
  active: NavItem;
  children: React.ReactNode;
}) {
  const navItems: Array<{ label: NavItem; href: string }> = [
    { label: "Assets", href: "/assets" },
    { label: "Workflows", href: "/workflows" },
    { label: "Runs", href: "/runs" },
    { label: "Explore", href: "/explore" },
    { label: "Labels", href: "/labels" },
    { label: "Admin", href: "/admin" },
  ];

  return (
    <div className="app-shell">
      <header className="app-header">
        <div className="app-header__group">
          <div className="app-header__pill">
            <span className="app-header__label">Workspace</span>
            <strong>{props.workspaceName}</strong>
          </div>
          <div className="app-header__pill">
            <span className="app-header__label">Project</span>
            <strong>{props.projectName}</strong>
          </div>
        </div>
        <div className="app-header__group">
          <span className="app-header__label">Runs</span>
          <span className="app-header__label">Local Dev</span>
        </div>
      </header>
      <aside className="app-sidebar">
        <ul>
          {navItems.map((item) => (
            <li key={item.label}>
              <a href={item.href} data-active={String(item.label === props.active)}>
                {item.label}
              </a>
            </li>
          ))}
        </ul>
      </aside>
      <main className="app-main">{props.children}</main>
    </div>
  );
}

function AssetsPage(props: {
  api: ApiClient;
  bootstrap: BootstrapContext;
}) {
  const [sourcePath, setSourcePath] = useState("");
  const [assets, setAssets] = useState<any[]>([]);
  const [error, setError] = useState<string | null>(null);

  const loadAssets = async () => {
    try {
      setAssets(await props.api.listAssets(props.bootstrap.project._id));
    } catch (loadError) {
      setError(loadError instanceof Error ? loadError.message : "Failed to load assets");
    }
  };

  useEffect(() => {
    void loadAssets();
  }, [props.bootstrap.project._id]);

  return (
    <div className="page-stack">
      <section className="panel">
        <h1>Assets</h1>
        <p>Register local folders, archives, or dataset files, then probe them into managed asset metadata.</p>
        <div className="field-grid">
          <label>
            Local Path
            <input
              value={sourcePath}
              onChange={(event) => setSourcePath(event.target.value)}
              placeholder="/Users/longtaowu/workspace/emboldata/data"
            />
          </label>
        </div>
        <div className="button-row" style={{ marginTop: 12 }}>
          <button
            className="button-primary"
            onClick={async () => {
              setError(null);
              try {
                const asset = await props.api.registerLocalAsset({
                  workspaceId: props.bootstrap.workspace._id,
                  projectId: props.bootstrap.project._id,
                  sourcePath,
                });
                await props.api.probeAsset(asset._id);
                await loadAssets();
              } catch (registerError) {
                setError(
                  registerError instanceof Error
                    ? registerError.message
                    : "Failed to register local asset",
                );
              }
            }}
          >
            Register Local Path
          </button>
        </div>
        {error ? <p>{error}</p> : null}
      </section>

      <section className="panel">
        <div className="list-grid">
          {assets.length === 0 ? (
            <p className="empty-state">No assets have been registered yet.</p>
          ) : (
            assets.map((asset) => (
              <article key={asset._id} className="asset-card">
                <div className="toolbar">
                  <a href={`/assets/${asset._id}`}>
                    <strong>{asset.displayName}</strong>
                  </a>
                  <span className="status-pill" data-status={asset.status}>
                    {asset.status}
                  </span>
                </div>
                <p>Type: {asset.type}</p>
                <p>Source: {asset.sourceType}</p>
                <p>Detected: {(asset.detectedFormats ?? []).join(", ") || "pending"}</p>
                <p>Top-level entries: {(asset.topLevelPaths ?? []).slice(0, 6).join(", ") || "n/a"}</p>
              </article>
            ))
          )}
        </div>
      </section>
    </div>
  );
}

function AssetDetailPage(props: {
  api: ApiClient;
  assetId: string;
}) {
  const [asset, setAsset] = useState<any | null>(null);
  const [probeReport, setProbeReport] = useState<any | null>(null);
  const [artifactId, setArtifactId] = useState<string | null>(null);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    void (async () => {
      try {
        const [assetData, reportData] = await Promise.all([
          props.api.getAsset(props.assetId),
          props.api.getProbeReport(props.assetId).catch(() => null),
        ]);
        setAsset(assetData);
        setProbeReport(reportData);
      } catch (loadError) {
        setError(loadError instanceof Error ? loadError.message : "Failed to load asset detail");
      }
    })();
  }, [props.assetId]);

  if (error) {
    return <section className="panel">{error}</section>;
  }
  if (!asset) {
    return <section className="panel">Loading asset detail...</section>;
  }

  return (
    <div className="page-stack">
      <section className="two-column">
        <div className="panel">
          <h1>{asset.displayName}</h1>
          <p>Asset ID: {asset._id}</p>
          <p>Type: {asset.type}</p>
          <p>Status: {asset.status}</p>
          <p>Source path: {asset.sourcePath ?? "n/a"}</p>
          <div className="button-row" style={{ marginTop: 12 }}>
            <button
              className="button-primary"
              onClick={async () => {
                const report = await props.api.probeAsset(asset._id);
                setProbeReport(report);
                const reloaded = await props.api.getAsset(asset._id);
                setAsset(reloaded);
              }}
            >
              Probe Again
            </button>
            <button
              className="button-secondary"
              onClick={async () => {
                if (!probeReport) {
                  return;
                }
                const artifact = await props.api.createArtifact({
                  type: "json",
                  title: `Probe Report: ${asset.displayName}`,
                  producerType: "asset",
                  producerId: asset._id,
                  payload: probeReport.rawReport ?? {},
                });
                setArtifactId(artifact._id);
              }}
            >
              Create Explore Artifact
            </button>
            {artifactId ? <a href={`/explore/${artifactId}`}>Open Explore View</a> : null}
          </div>
        </div>

        <aside className="panel">
          <h2>Probe Summary</h2>
          {probeReport ? (
            <>
              <p>Detected: {(probeReport.detectedFormatCandidates ?? []).join(", ")}</p>
              <p>Warnings: {(probeReport.warnings ?? []).join(", ") || "none"}</p>
              <p>
                Recommended nodes: {(probeReport.recommendedNextNodes ?? []).join(", ") || "inspect_asset"}
              </p>
              <pre className="mono-block">
                {JSON.stringify(probeReport.structureSummary ?? {}, null, 2)}
              </pre>
            </>
          ) : (
            <p className="empty-state">No probe report yet.</p>
          )}
        </aside>
      </section>
    </div>
  );
}

function WorkflowsPage(props: {
  api: ApiClient;
  bootstrap: BootstrapContext;
}) {
  const [workflows, setWorkflows] = useState<any[]>([]);
  const [error, setError] = useState<string | null>(null);

  const load = async () => {
    try {
      setWorkflows(await props.api.listWorkflows(props.bootstrap.project._id));
    } catch (loadError) {
      setError(loadError instanceof Error ? loadError.message : "Failed to load workflows");
    }
  };

  useEffect(() => {
    void load();
  }, [props.bootstrap.project._id]);

  return (
    <div className="page-stack">
      <section className="panel">
        <div className="toolbar">
          <h1 style={{ margin: 0 }}>Workflows</h1>
          <button
            className="button-primary"
            onClick={async () => {
              await props.api.createWorkflow({
                workspaceId: props.bootstrap.workspace._id,
                projectId: props.bootstrap.project._id,
                name: `Delivery Normalize ${workflows.length + 1}`,
              });
              await load();
            }}
          >
            Create Workflow
          </button>
        </div>
        {error ? <p>{error}</p> : null}
      </section>

      <section className="panel">
        <div className="list-grid">
          {workflows.length === 0 ? (
            <p className="empty-state">No workflows yet.</p>
          ) : (
            workflows.map((workflow) => (
              <article key={workflow._id} className="asset-card">
                <a href={`/workflows/${workflow._id}`}>
                  <strong>{workflow.name}</strong>
                </a>
                <p>Status: {workflow.status}</p>
                <p>Latest version: {workflow.latestVersionNumber}</p>
              </article>
            ))
          )}
        </div>
      </section>
    </div>
  );
}

function WorkflowEditorPage(props: {
  api: ApiClient;
  workflowId: string;
}) {
  const [workflow, setWorkflow] = useState<any | null>(null);
  const [nodes, setNodes] = useState<any[]>([]);
  const [assets, setAssets] = useState<any[]>([]);
  const [selectedAssetId, setSelectedAssetId] = useState<string | null>(null);
  const [versions, setVersions] = useState<any[]>([]);
  const [draft, setDraft] = useState<WorkflowDraft>(() => createDefaultWorkflowDraft());
  const [selectedNodeId, setSelectedNodeId] = useState("rename-folder");
  const [lastRunId, setLastRunId] = useState<string | null>(null);
  const [dirty, setDirty] = useState(false);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    void (async () => {
      try {
        const workflowDefinition = await props.api.getWorkflowDefinition(props.workflowId);
        const [nodeDefs, workflowVersions, workflowAssets] = await Promise.all([
          props.api.listNodeDefinitions(),
          props.api.listWorkflowVersions(props.workflowId),
          props.api.listAssets(workflowDefinition.projectId),
        ]);
        setWorkflow(workflowDefinition);
        setNodes(nodeDefs);
        setAssets(workflowAssets);
        setSelectedAssetId((previous) => {
          if (previous && workflowAssets.some((asset) => asset._id === previous)) {
            return previous;
          }
          return workflowAssets[0]?._id ?? null;
        });
        setVersions(workflowVersions);
        const nextDraft = workflowDraftFromVersion(workflowVersions[0] ?? null);
        setDraft(nextDraft);
        setSelectedNodeId(nextDraft.logicGraph.nodes[0]?.id ?? "rename-folder");
        setDirty(false);
      } catch (loadError) {
        setError(loadError instanceof Error ? loadError.message : "Failed to load workflow");
      }
    })();
  }, [props.workflowId]);

  const selectedNode = useMemo(
    () =>
      nodes.find((node) => node.id === resolveDefinitionIdForNode(draft, selectedNodeId)) ?? null,
    [draft, nodes, selectedNodeId],
  );

  async function saveCurrentDraft() {
    const version = await props.api.saveWorkflowVersion(
      props.workflowId,
      serializeWorkflowDraft(draft),
    );
    setVersions((previous) => [version, ...previous.filter((item) => item._id !== version._id)]);
    setDirty(false);
    return version;
  }

  return (
    <div className="page-stack">
      <section className="panel">
        <div className="toolbar">
          <h1 style={{ margin: 0 }}>{workflow?.name ?? "Workflow Editor"}</h1>
          <label style={{ display: "flex", alignItems: "center", gap: 8 }}>
            <span>Run Asset</span>
            <select
              value={selectedAssetId ?? ""}
              onChange={(event) => setSelectedAssetId(event.target.value || null)}
            >
              {assets.length === 0 ? <option value="">No assets available</option> : null}
              {assets.map((asset) => (
                <option key={asset._id} value={asset._id}>
                  {asset.displayName}
                </option>
              ))}
            </select>
          </label>
          <button
            className="button-primary"
            onClick={async () => {
              await saveCurrentDraft();
            }}
          >
            Save Workflow Version
          </button>
          <button
            className="button-secondary"
            onClick={async () => {
              if (!selectedAssetId) {
                setError("Select an asset before triggering a workflow run.");
                return;
              }
              const latestVersion = dirty || versions.length === 0
                ? await saveCurrentDraft()
                : versions[0];
              const run = await props.api.createRun({
                workflowDefinitionId: props.workflowId,
                workflowVersionId: latestVersion._id,
                assetIds: [selectedAssetId],
              });
              setLastRunId(run._id);
            }}
            disabled={!selectedAssetId}
          >
            Trigger Workflow Run
          </button>
          <button
            className="button-secondary"
            onClick={() => {
              const latestDraft = workflowDraftFromVersion(versions[0] ?? null);
              setDraft(latestDraft);
              setSelectedNodeId(latestDraft.logicGraph.nodes[0]?.id ?? "rename-folder");
              setDirty(false);
            }}
          >
            Reload Latest Saved
          </button>
          {lastRunId ? <a href={`/runs/${lastRunId}`}>Open Latest Run</a> : null}
        </div>
        {error ? <p>{error}</p> : null}
      </section>

      <section className="editor-layout">
        <aside className="panel">
          <h2>Node Library</h2>
          <div className="list-grid">
            {nodes.map((node) => (
              <button
                key={node.id}
                className="button-secondary"
                onClick={() => {
                  const result = addNodeToDraft(draft, node);
                  setDraft(result.draft);
                  setSelectedNodeId(result.nodeId);
                  setDirty(true);
                }}
              >
                {node.name}
              </button>
            ))}
          </div>
        </aside>

        <section className="panel">
          <h2>Canvas</h2>
          <div className="list-grid">
            {draft.logicGraph.nodes.map((node) => (
              <div
                key={node.id}
                className="node-card"
                data-selected={String(selectedNodeId === node.id)}
                onClick={() => setSelectedNodeId(node.id)}
                style={{ cursor: "pointer" }}
              >
                <strong>{node.id}</strong>
                <p>Type: {node.type}</p>
                <p>Definition: {resolveDefinitionIdForNode(draft, node.id)}</p>
                <button
                  className="button-secondary"
                  onClick={(event) => {
                    event.preventDefault();
                    event.stopPropagation();
                    const next = removeNodeFromDraft(draft, node.id);
                    setDraft(next);
                    setSelectedNodeId(next.logicGraph.nodes[0]?.id ?? "");
                    setDirty(true);
                  }}
                >
                  Remove Node
                </button>
              </div>
            ))}
          </div>
          <p style={{ marginTop: 12 }}>
            Latest saved versions: {versions.length > 0 ? versions.map((item) => item.versionNumber).join(", ") : "none"}
          </p>
          <p>Draft status: {dirty ? "unsaved changes" : "synced"}</p>
        </section>

        <aside className="panel">
          <h2>Node Configuration</h2>
          {selectedNode ? (
            <>
              <p><strong>{selectedNode.name}</strong></p>
              <p>{selectedNode.description}</p>
              <p>Category: {selectedNode.category}</p>
            </>
          ) : (
            <p className="empty-state">Select a node.</p>
          )}
        </aside>
      </section>
    </div>
  );
}

function RunsIndexPage() {
  return (
    <section className="panel">
      <h1>Runs</h1>
      <p className="empty-state">Open a specific run from the workflow editor.</p>
    </section>
  );
}

function RunDetailPage(props: {
  api: ApiClient;
  runId: string;
}) {
  const [run, setRun] = useState<any | null>(null);
  const [tasks, setTasks] = useState<any[]>([]);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    void (async () => {
      try {
        const [runData, taskData] = await Promise.all([
          props.api.getRun(props.runId),
          props.api.listRunTasks(props.runId),
        ]);
        setRun(runData);
        setTasks(taskData);
      } catch (loadError) {
        setError(loadError instanceof Error ? loadError.message : "Failed to load run detail");
      }
    })();
  }, [props.runId]);

  if (error) {
    return <section className="panel">{error}</section>;
  }
  if (!run) {
    return <section className="panel">Loading run...</section>;
  }

  return (
    <div className="page-stack">
      <section className="panel">
        <h1>Run Detail</h1>
        <p>Run ID: {run._id}</p>
        <p>Status: {run.status}</p>
        <p>
          Input assets:{" "}
          {(run.assetIds ?? []).length > 0
            ? run.assetIds.map((assetId: string) => assetId).join(", ")
            : "none"}
        </p>
      </section>
      <section className="two-column">
        <div className="panel">
          <h2>Run Graph</h2>
          <div className="list-grid">
            {tasks.map((task) => (
              <article key={task._id} className="task-card">
                <div className="toolbar">
                  <strong>{task.nodeId}</strong>
                  <span className="status-pill" data-status={task.status}>
                    {task.status}
                  </span>
                </div>
                <p>Node type: {task.nodeType}</p>
                <p>Bound assets: {(task.assetIds ?? []).join(", ") || "none"}</p>
              </article>
            ))}
          </div>
        </div>
        <aside className="panel">
          <h2>Selected Task</h2>
          {tasks[0] ? (
            <>
              <p>Node: {tasks[0].nodeId}</p>
              <p>Status: {tasks[0].status}</p>
              <pre className="mono-block">{JSON.stringify(tasks[0], null, 2)}</pre>
            </>
          ) : (
            <p className="empty-state">No tasks created.</p>
          )}
        </aside>
      </section>
    </div>
  );
}

function ExplorePage(props: {
  api: ApiClient;
  artifactId: string | null;
}) {
  const [artifact, setArtifact] = useState<any | null>(null);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    if (!props.artifactId) {
      return;
    }
    void (async () => {
      try {
        setArtifact(await props.api.getArtifact(props.artifactId!));
      } catch (loadError) {
        setError(loadError instanceof Error ? loadError.message : "Failed to load artifact");
      }
    })();
  }, [props.artifactId]);

  if (!props.artifactId) {
    return (
      <section className="panel">
        <h1>Explore</h1>
        <p className="empty-state">Create an artifact from asset detail to inspect it here.</p>
      </section>
    );
  }

  if (error) {
    return <section className="panel">{error}</section>;
  }
  if (!artifact) {
    return <section className="panel">Loading artifact...</section>;
  }

  return (
    <div className="page-stack">
      <section className="panel">
        <h1>{artifact.title}</h1>
        <p>Artifact ID: {artifact._id}</p>
        <p>Type: {artifact.type}</p>
        <pre className="mono-block">{JSON.stringify(artifact.payload, null, 2)}</pre>
      </section>
    </div>
  );
}

export function App(props: AppProps) {
  const api = useMemo(() => new ApiClient(props.apiBaseUrl), [props.apiBaseUrl]);
  const pathname = usePathname();
  const [bootstrap, setBootstrap] = useState<BootstrapContext | null>(null);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    void (async () => {
      try {
        setBootstrap(await api.bootstrapDev());
      } catch (bootstrapError) {
        setError(
          bootstrapError instanceof Error ? bootstrapError.message : "Failed to bootstrap local context",
        );
      }
    })();
  }, [api]);

  if (error) {
    return <section className="panel">{error}</section>;
  }
  if (!bootstrap) {
    return <section className="panel">Bootstrapping local workspace...</section>;
  }

  const assetMatch = pathname.match(/^\/assets\/([^/]+)$/);
  const workflowMatch = pathname.match(/^\/workflows\/([^/]+)$/);
  const runMatch = pathname.match(/^\/runs\/([^/]+)$/);
  const exploreMatch = pathname.match(/^\/explore\/([^/]+)$/);

  let active: NavItem = "Assets";
  let content: React.ReactNode = <AssetsPage api={api} bootstrap={bootstrap} />;

  if (pathname === "/workflows") {
    active = "Workflows";
    content = <WorkflowsPage api={api} bootstrap={bootstrap} />;
  } else if (workflowMatch) {
    active = "Workflows";
    content = <WorkflowEditorPage api={api} workflowId={workflowMatch[1]} />;
  } else if (pathname === "/runs") {
    active = "Runs";
    content = <RunsIndexPage />;
  } else if (runMatch) {
    active = "Runs";
    content = <RunDetailPage api={api} runId={runMatch[1]} />;
  } else if (exploreMatch) {
    active = "Explore";
    content = <ExplorePage api={api} artifactId={exploreMatch[1]} />;
  } else if (pathname === "/explore") {
    active = "Explore";
    content = <ExplorePage api={api} artifactId={null} />;
  } else if (assetMatch) {
    active = "Assets";
    content = <AssetDetailPage api={api} assetId={assetMatch[1]} />;
  }

  return (
    <AppShell
      workspaceName={bootstrap.workspace.name}
      projectName={bootstrap.project.name}
      active={active}
    >
      {content}
    </AppShell>
  );
}
