import { useMemo } 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 ?? {}
  };
}

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

  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 />
        <RightInspector model={model} selection={selection} compileResult={compileResult} />
      </div>
    </div>
  );
}
