import { useEffect, useMemo, useState } from "react";
import { analyze, autoLayout, compile, tidyLayout } from "./api/client";
import { CanvasArea } from "./components/CanvasArea";
import { LeftPanel } from "./components/LeftPanel";
import { RightInspector } from "./components/RightInspector";
import { TopBar } from "./components/TopBar";
import type { TopBarStatusTone } from "./components/TopBar";
import sampleFixture from "./fixtures/sample.schemeta.json";
import { useSchemetaActions, useSchemetaSelector } from "./hooks";
import { createSchemetaStore } from "./state";
import type { SchemetaModel } from "./state";

type EndpointActionId = "compile" | "analyze" | "layout-auto" | "layout-tidy";
type LocalActionId = "load-sample" | "reset-sample";
type WorkflowActionId = EndpointActionId | LocalActionId;

type WorkflowAction = {
  id: WorkflowActionId;
  label: string;
};

const SAMPLE_PAYLOAD = sampleFixture as SchemetaModel;

function getPayloadOrThrow(payload: unknown): SchemetaModel {
  if (typeof payload !== "object" || payload === null) {
    throw new Error("No model loaded. Run 'Load Sample JSON' first.");
  }
  return payload as SchemetaModel;
}

function toCompileFromAnalyze(result: Awaited<ReturnType<typeof analyze>>): Record<string, unknown> {
  return {
    api_version: result.api_version,
    schema_version: result.schema_version,
    request_id: result.request_id,
    ok: result.ok ?? true,
    errors: Array.isArray(result.errors) ? result.errors : [],
    warnings: Array.isArray(result.warnings) ? result.warnings : [],
    topology: result.topology ?? {}
  };
}

function sortKeysDeep(value: unknown): unknown {
  if (Array.isArray(value)) {
    return value.map((item) => sortKeysDeep(item));
  }
  if (value && typeof value === "object") {
    const record = value as Record<string, unknown>;
    const sorted = Object.keys(record)
      .sort((a, b) => a.localeCompare(b))
      .reduce<Record<string, unknown>>((acc, key) => {
        acc[key] = sortKeysDeep(record[key]);
        return acc;
      }, {});
    return sorted;
  }
  return value;
}

export function App() {
  const store = useMemo(() => createSchemetaStore({ model: SAMPLE_PAYLOAD }), []);
  const actionsApi = useSchemetaActions(store);
  const model = useSchemetaSelector(store, (state) => state.model);
  const selection = useSchemetaSelector(store, (state) => state.selection);
  const compileResult = useSchemetaSelector(store, (state) => state.compileResult);
  const lifecycle = useSchemetaSelector(store, (state) => state.lifecycle);
  const jsonError = useSchemetaSelector(store, (state) => state.uiFlags.jsonError);
  const [jsonEditorText, setJsonEditorText] = useState(() => JSON.stringify(SAMPLE_PAYLOAD, null, 2));

  useEffect(() => {
    if (model) {
      setJsonEditorText(JSON.stringify(model, null, 2));
    }
  }, [model]);
  const lastApiVersion = useSchemetaSelector(store, (state) => {
    const apiVersion = state.compileResult && typeof state.compileResult.api_version === "string" ? state.compileResult.api_version : null;
    return apiVersion ?? "-";
  });

  const actions = useMemo<WorkflowAction[]>(
    () => [
      { id: "load-sample", label: "Load Sample JSON" },
      { id: "compile", label: "Compile" },
      { id: "analyze", label: "Analyze" },
      { id: "layout-auto", label: "Auto Layout" },
      { id: "layout-tidy", label: "Auto Tidy" },
      { id: "reset-sample", label: "Reset Sample" }
    ],
    []
  );

  const status = jsonError
    ? `JSON parse failed: ${jsonError}`
    : lifecycle.isCompiling
      ? `${lifecycle.lastAction ?? "Working"}...`
      : lifecycle.lastError
        ? `${lifecycle.lastAction ?? "Action"} failed: ${lifecycle.lastError}`
        : lifecycle.lastAction
          ? `${lifecycle.lastAction} complete`
          : "Idle";
  const statusTone: TopBarStatusTone = jsonError
    ? "error"
    : lifecycle.isCompiling
      ? "busy"
      : lifecycle.lastError
        ? "error"
        : lifecycle.lastAction
          ? "success"
          : "idle";

  async function runAction(actionId: WorkflowActionId) {
    const action = actions.find((item) => item.id === actionId);
    if (!action) {
      return;
    }

    actionsApi.beginCompile(action.label);
    try {
      if (action.id === "load-sample" || action.id === "reset-sample") {
        const parsed = actionsApi.applyJsonText(JSON.stringify(SAMPLE_PAYLOAD));
        if (!parsed.ok) {
          throw new Error(parsed.error);
        }
        actionsApi.setCompileResult(null);
        actionsApi.setSelection({ selectedRefs: [], selectedNet: null, selectedPin: null });
        actionsApi.setViewport({ scale: 1, panX: 40, panY: 40 });
        actionsApi.completeCompile(action.label);
        return;
      }

      const payload = getPayloadOrThrow(store.getState().model);

      switch (action.id) {
        case "compile":
          {
            const result = await compile(payload, { preserve_placement: true });
            actionsApi.setCompileResult(result);
          }
          break;
        case "analyze":
          {
            const result = await analyze(payload);
            actionsApi.setCompileResult(toCompileFromAnalyze(result));
          }
          break;
        case "layout-auto":
          {
            const result = await autoLayout(payload);
            if (result.model) {
              actionsApi.setModel(result.model);
            }
            if (result.compile) {
              actionsApi.setCompileResult(result.compile);
            }
          }
          break;
        case "layout-tidy":
          {
            const result = await tidyLayout(payload);
            if (result.model) {
              actionsApi.setModel(result.model);
            }
            if (result.compile) {
              actionsApi.setCompileResult(result.compile);
            }
          }
          break;
      }

      actionsApi.completeCompile(action.label);
    } catch (err) {
      actionsApi.failCompile(action.label, err);
    }
  }

  function applyJsonFromEditor() {
    const parsed = actionsApi.applyJsonText(jsonEditorText);
    if (!parsed.ok) {
      actionsApi.failCompile("apply-json", parsed.error);
      return;
    }
    actionsApi.completeCompile("apply-json");
  }

  async function validateJsonEditor() {
    const parsed = actionsApi.applyJsonText(jsonEditorText);
    if (!parsed.ok) {
      actionsApi.failCompile("validate-json", parsed.error);
      return;
    }

    const modelCandidate = store.getState().model;
    if (!modelCandidate) {
      actionsApi.failCompile("validate-json", "No model loaded");
      return;
    }

    actionsApi.beginCompile("validate-json");
    try {
      const analyzed = await analyze(modelCandidate);
      actionsApi.setCompileResult(toCompileFromAnalyze(analyzed));
      actionsApi.completeCompile("validate-json");
    } catch (error) {
      actionsApi.failCompile("validate-json", error);
    }
  }

  function formatJsonEditor() {
    const parsed = actionsApi.applyJsonText(jsonEditorText);
    if (!parsed.ok) {
      actionsApi.failCompile("format-json", parsed.error);
      return;
    }
    const next = store.getState().model;
    if (next) {
      setJsonEditorText(JSON.stringify(next, null, 2));
    }
    actionsApi.completeCompile("format-json");
  }

  function sortJsonEditorKeys() {
    try {
      const parsed = JSON.parse(jsonEditorText);
      const sorted = sortKeysDeep(parsed);
      setJsonEditorText(JSON.stringify(sorted, null, 2));
      actionsApi.completeCompile("sort-json");
    } catch (error) {
      actionsApi.failCompile("sort-json", error);
    }
  }

  async function copyReproJson() {
    try {
      await navigator.clipboard.writeText(jsonEditorText);
      actionsApi.completeCompile("copy-repro");
    } catch {
      actionsApi.failCompile("copy-repro", "Clipboard write failed");
    }
  }

  return (
    <div className="app-shell">
      <TopBar
        title="React Frontend Skeleton"
        actions={actions.map((action) => ({
          id: action.id,
          label: action.label,
          onClick: () => {
            void runAction(action.id);
          },
          disabled: lifecycle.isCompiling
        }))}
        statusMessage={status}
        statusTone={statusTone}
        apiVersion={lastApiVersion}
      />
      <div className="workspace-grid">
        <LeftPanel
          model={model}
          selection={selection}
          onSelectInstance={(ref) =>
            actionsApi.setSelection({
              selectedRefs: selection.selectedRefs.includes(ref) ? [] : [ref],
              selectedNet: null,
              selectedPin: null
            })
          }
          onSelectNet={(name) =>
            actionsApi.setSelection({
              selectedRefs: [],
              selectedNet: selection.selectedNet === name ? null : name,
              selectedPin: null
            })
          }
        />
        <CanvasArea store={store} />
        <RightInspector
          model={model}
          selection={selection}
          compileResult={compileResult}
          jsonText={jsonEditorText}
          jsonError={jsonError}
          onJsonTextChange={setJsonEditorText}
          onApplyJson={applyJsonFromEditor}
          onValidateJson={() => {
            void validateJsonEditor();
          }}
          onFormatJson={formatJsonEditor}
          onSortJson={sortJsonEditorKeys}
          onCopyRepro={() => {
            void copyReproJson();
          }}
        />
      </div>
    </div>
  );
}
