import type * as React from "react";
import { describe, expect, expectTypeOf, it } from "vitest";

import * as ToolcraftRuntime from "../../index";
import type { ToolcraftHistoryPatch } from "../../index";
import type { ToolcraftRendererPipelineClient } from "../../rendering";
import type { ToolcraftCommand } from "../../state/types";
import * as ToolcraftReact from "../index";
import {
  isToolcraftLayerVisibleInTree,
  useToolcraftMediaPresentationUrls,
  useToolcraftProductSceneFrame,
} from "../index";
import { CanvasShell } from "../canvas/canvas-shell";
import { ControlsPanel } from "../controls-panel/controls-panel";
import type { ToolcraftPanelActionContext } from "../controls-panel/actions/controls-panel-actions";
import {
  ToolcraftApp,
  type ToolcraftAppComposition,
} from "./toolcraft-app";
import { useToolcraftStore } from "./toolcraft-store-context";
import {
  ToolcraftRoot,
  type ToolcraftContextValue,
} from "./toolcraft-root";
import { runtimePublicApiSchema } from "./runtime-public-api-test-support";
import { LayersPanel } from "../layers/layers-panel";
import { PanelContainer, PanelHost, PanelStage } from "../panel-host/panel-host";
import { TimelinePanel } from "../timeline/timeline-panel";
import { ToolbarPanel } from "./toolbar-panel";
import {
  useToolcraftDispatch,
  useToolcraftSelector,
} from "./use-toolcraft";
import { useToolcraftPipeline } from "./use-toolcraft-pipeline";
import { useToolcraftPipelinePass } from "./use-toolcraft-pipeline-pass";

describe("Toolcraft template runtime public API", () => {
  it("exports selector hooks without exposing the internal store context", () => {
    expect(ToolcraftRuntime).not.toHaveProperty(
      "toolcraftControlsResetHistorySource",
    );
    expect(ToolcraftRuntime.TOOLCRAFT_SVG_NAMESPACE).toBe(
      "http://www.w3.org/2000/svg",
    );
    expect(ToolcraftRuntime).not.toHaveProperty("exportToolcraftSvgArtifact");
    expect(ToolcraftRuntime).not.toHaveProperty("createToolcraftSvgDocument");
    expect(ToolcraftRuntime).not.toHaveProperty(
      "serializeToolcraftSvgDocument",
    );
    expect(ToolcraftReact.useToolcraftDispatch).toBe(useToolcraftDispatch);
    expect(ToolcraftReact.useToolcraftSelector).toBe(useToolcraftSelector);
    expect(ToolcraftReact.useToolcraftPipeline).toBe(useToolcraftPipeline);
    expect(ToolcraftReact.useToolcraftPipelinePass).toBe(
      useToolcraftPipelinePass,
    );
    expect(ToolcraftReact.useToolcraftMediaPresentationUrls).toBe(
      useToolcraftMediaPresentationUrls,
    );
    expect(useToolcraftMediaPresentationUrls).toEqual(expect.any(Function));
    expect(ToolcraftReact.isToolcraftLayerVisibleInTree).toBe(
      isToolcraftLayerVisibleInTree,
    );
    expect(isToolcraftLayerVisibleInTree).toEqual(expect.any(Function));
    expect(ToolcraftReact.useToolcraftProductSceneFrame).toBe(
      useToolcraftProductSceneFrame,
    );
    expect(useToolcraftProductSceneFrame).toEqual(expect.any(Function));
    expect(ToolcraftReact).not.toHaveProperty("ToolcraftStoreContext");
    expect(ToolcraftReact).not.toHaveProperty("useToolcraftStore");
  });

  it("keeps app assembly props narrow", () => {
    expectTypeOf<keyof ToolcraftHistoryPatch>().toEqualTypeOf<
      "after" | "before" | "group" | "label"
    >();
    expectTypeOf<keyof ToolcraftContextValue>().toEqualTypeOf<
      "dispatch" | "state"
    >();
    expectTypeOf<ReturnType<typeof useToolcraftDispatch>>().toEqualTypeOf<
      React.Dispatch<ToolcraftCommand>
    >();
    expectTypeOf<keyof ToolcraftAppComposition>().toEqualTypeOf<
      | "canvasContent"
      | "controlRenderers"
      | "exportRenderer"
      | "infiniteCanvasContent"
      | "modelPresentation"
      | "onPanelAction"
      | "renderDefaultCanvasMedia"
      | "rendererPipelineRegistration"
      | "sceneBoundsProvider"
      | "schema"
      | "svgExportRenderer"
    >();
    expectTypeOf<
      keyof React.ComponentProps<typeof ToolcraftApp>
    >().toEqualTypeOf<
      | "canvasContent"
      | "className"
      | "controlRenderers"
      | "exportRenderer"
      | "infiniteCanvasContent"
      | "modelPresentation"
      | "onPanelAction"
      | "renderDefaultCanvasMedia"
      | "rendererPipelineRegistration"
      | "sceneBoundsProvider"
      | "schema"
      | "style"
      | "svgExportRenderer"
    >();
    expectTypeOf<
      keyof React.ComponentProps<typeof ToolcraftRoot>
    >().toEqualTypeOf<
      | "children"
      | "initialState"
      | "modelPresentation"
      | "rendererPipelineRegistration"
      | "schema"
    >();
    expectTypeOf<ReturnType<typeof useToolcraftPipeline>>().toEqualTypeOf<
      ToolcraftRendererPipelineClient | null
    >();
    expectTypeOf<keyof ToolcraftPanelActionContext>().toEqualTypeOf<
      | "action"
      | "dispatch"
      | "reportFeedback"
      | "resolveMediaResource"
      | "rendererPipeline"
      | "reportProgress"
      | "state"
    >();
    if (false) {
      const client = useToolcraftPipeline();
      if (client) {
        // @ts-expect-error Product-facing pipeline clients cannot close shared runtime ownership.
        void client.dispose();
      }
    }
  });

  it("keeps runtime panel props focused on host integration", () => {
    type RuntimePanelKeys =
      | "className"
      | "framed"
      | "onPanelStateChange"
      | "panelPlacement"
      | "panelState";
    type ControlsPanelKeys =
      | RuntimePanelKeys
      | "controlRenderers"
      | "onPanelAction"
      | "sceneExport";

    expectTypeOf<
      keyof React.ComponentProps<typeof ControlsPanel>
    >().toEqualTypeOf<ControlsPanelKeys>();
    expectTypeOf<
      keyof React.ComponentProps<typeof TimelinePanel>
    >().toEqualTypeOf<RuntimePanelKeys | "defaultExpanded" | "variant">();
    expectTypeOf<
      keyof React.ComponentProps<typeof ToolbarPanel>
    >().toEqualTypeOf<RuntimePanelKeys>();
    expectTypeOf<
      keyof React.ComponentProps<typeof LayersPanel>
    >().toEqualTypeOf<RuntimePanelKeys | "groupCreation">();
  });

  it("keeps shell and panel host props separate", () => {
    expectTypeOf<
      keyof React.ComponentProps<typeof CanvasShell>
    >().toEqualTypeOf<
      "children" | "infiniteCanvasContent" | "renderDefaultMedia"
    >();
    expectTypeOf<React.ComponentProps<typeof PanelHost>>().toHaveProperty(
      "panelType",
    );
    expectTypeOf<React.ComponentProps<typeof PanelContainer>>().toHaveProperty(
      "placement",
    );
    expectTypeOf<React.ComponentProps<typeof PanelStage>>().toHaveProperty(
      "children",
    );
  });

  it("rejects hidden runtime wiring props at compile time", () => {
    expect(<ToolcraftApp schema={runtimePublicApiSchema} />).toBeTruthy();
    expect(<CanvasShell />).toBeTruthy();
    expect(<ToolbarPanel />).toBeTruthy();

    const appWithInitialStateProps: React.ComponentProps<
      typeof ToolcraftApp
    > = {
      // @ts-expect-error Runtime state seeding belongs to ToolcraftRoot, not the app shell.
      initialState: {},
      schema: runtimePublicApiSchema,
    };
    const appWithChildrenProps: React.ComponentProps<typeof ToolcraftApp> =
      {
        // @ts-expect-error ToolcraftApp is assembled from schema and does not accept children.
        children: "Child",
        schema: runtimePublicApiSchema,
      };
    const appWithToolbarThemeToggleProps: React.ComponentProps<
      typeof ToolcraftApp
    > = {
      schema: runtimePublicApiSchema,
      // @ts-expect-error Toolbar theme visibility belongs to schema.toolbar.theme.
      toolbarThemeToggle: false,
    };
    const appWithCanvasSlotProps: React.ComponentProps<typeof ToolcraftApp> =
      {
        canvasContent: <div />,
        onPanelAction: ({ action }) => {
          void action.value;
        },
        renderDefaultCanvasMedia: false,
        schema: runtimePublicApiSchema,
      };
    const compositionWithClassName: ToolcraftAppComposition = {
      schema: runtimePublicApiSchema,
      // @ts-expect-error Host layout classes belong to the protected route.
      className: "product-shell",
    };
    const compositionWithStyle: ToolcraftAppComposition = {
      schema: runtimePublicApiSchema,
      // @ts-expect-error Host layout styles belong to the protected route.
      style: { minHeight: 0 },
    };
    const canvasWithSlotProps: React.ComponentProps<typeof CanvasShell> = {
      children: <div />,
      infiniteCanvasContent: <div />,
      renderDefaultMedia: false,
    };
    // @ts-expect-error Canvas upload belongs to schema.canvas.upload.
    const canvasWithUpload = <CanvasShell upload />;
    // @ts-expect-error CanvasShell is a runtime-owned shell, not a styled wrapper.
    const styledCanvas = <CanvasShell className="custom" />;
    // @ts-expect-error Toolbar history visibility belongs to schema.toolbar.history.
    const toolbarWithHistory = <ToolbarPanel history />;
    // @ts-expect-error Toolbar theme visibility belongs to schema.toolbar.theme.
    const toolbarWithThemeToggle = <ToolbarPanel themeToggle={false} />;

    expect(appWithInitialStateProps).toBeTruthy();
    expect(appWithChildrenProps).toBeTruthy();
    expect(appWithToolbarThemeToggleProps).toBeTruthy();
    expect(appWithCanvasSlotProps).toBeTruthy();
    expect(compositionWithClassName).toBeTruthy();
    expect(compositionWithStyle).toBeTruthy();
    expect(canvasWithSlotProps).toBeTruthy();
    expect(canvasWithUpload).toBeTruthy();
    expect(styledCanvas).toBeTruthy();
    expect(toolbarWithHistory).toBeTruthy();
    expect(toolbarWithThemeToggle).toBeTruthy();
  });

});
