import { act, cleanup, render } from "@testing-library/react";
import * as React from "react";
import { afterEach, describe, expect, it, vi } from "vitest";

import { defineToolcraft } from "../../../schema/define-toolcraft";
import type { ToolcraftExternalStore } from "../../../state/toolcraft-external-store";
import { ToolcraftRoot } from "../../app-shell/toolcraft-root";
import { useToolcraftStore } from "../../app-shell/toolcraft-store-context";
import { ControlsPanel } from "../controls-panel";

const counters = vi.hoisted(() => ({
  commits: new Map<string, number>(),
  executions: new Map<string, number>(),
}));

function increment(counter: Map<string, number>, id: string): void {
  counter.set(id, (counter.get(id) ?? 0) + 1);
}

function CommitProbe({ id }: { id: string }): null {
  React.useLayoutEffect(() => {
    increment(counters.commits, id);
  });

  return null;
}

vi.mock("@repo/ui", async (importOriginal) => {
  const actual = await importOriginal<typeof import("@repo/ui")>();

  return {
    ...actual,
    PanelSection: (props: React.ComponentProps<typeof actual.PanelSection>) => (
      <>
        <CommitProbe id={`section:${String(props.title)}`} />
        <actual.PanelSection {...props} />
      </>
    ),
  };
});

vi.mock("../renderers/controls-panel-basic-renderers", async (importOriginal) => {
  const actual = await importOriginal<
    typeof import("../renderers/controls-panel-basic-renderers")
  >();

  return {
    ...actual,
    renderBasicControl: (
      args: Parameters<typeof actual.renderBasicControl>[0],
    ): React.ReactNode => {
      increment(counters.executions, args.id);

      return (
        <>
          <CommitProbe id={`control:${args.id}`} />
          {actual.renderBasicControl(args)}
        </>
      );
    },
  };
});

vi.mock("../renderers/controls-panel-compound-renderers", async (importOriginal) => {
  const actual = await importOriginal<
    typeof import("../renderers/controls-panel-compound-renderers")
  >();

  return {
    ...actual,
    renderCompoundColorGroup: (
      args: Parameters<typeof actual.renderCompoundColorGroup>[0],
    ): React.ReactNode => {
      const id = args.entries.map(([controlId]) => controlId).join("+");
      increment(counters.executions, id);

      return (
        <>
          <CommitProbe id={`control:${id}`} />
          {actual.renderCompoundColorGroup(args)}
        </>
      );
    },
  };
});

function CaptureStore({
  capture,
}: {
  capture: (store: ToolcraftExternalStore) => void;
}): null {
  capture(useToolcraftStore());
  return null;
}

function resetCounters(): void {
  counters.commits.clear();
  counters.executions.clear();
}

function count(counter: Map<string, number>, id: string): number {
  return counter.get(id) ?? 0;
}

function renderIsolationPanel(
  schema: ReturnType<typeof defineToolcraft>,
  initialState?: React.ComponentProps<typeof ToolcraftRoot>["initialState"],
): ToolcraftExternalStore {
  let store: ToolcraftExternalStore | undefined;

  render(
    <ToolcraftRoot initialState={initialState} schema={schema}>
      <CaptureStore capture={(value) => { store = value; }} />
      <ControlsPanel framed={false} />
    </ToolcraftRoot>,
  );

  if (!store) {
    throw new Error("Expected Toolcraft store to be captured.");
  }

  resetCounters();
  return store;
}

afterEach(() => {
  cleanup();
  resetCounters();
  window.localStorage.clear();
});

describe("ControlsPanel render isolation", () => {
  it("commits only the slider whose target changed", () => {
    const schema = defineToolcraft({
      canvas: { enabled: true },
      panels: {
        controls: {
          sections: [
            {
              controls: {
                alpha: {
                  defaultValue: 10,
                  label: "Alpha",
                  max: 100,
                  min: 0,
                  target: "shape.alpha",
                  type: "slider",
                },
                beta: {
                  defaultValue: 20,
                  label: "Beta",
                  max: 100,
                  min: 0,
                  target: "shape.beta",
                  type: "slider",
                },
              },
              title: "Transform",
            },
          ],
          title: "Controls",
        },
      },
    });
    const store = renderIsolationPanel(schema);

    act(() => {
      store.dispatch({
        target: "shape.alpha",
        type: "controls.setValue",
        value: 30,
      });
    });

    expect(count(counters.executions, "alpha")).toBe(1);
    expect(count(counters.commits, "control:alpha")).toBe(1);
    expect(count(counters.executions, "beta")).toBe(0);
    expect(count(counters.commits, "control:beta")).toBe(0);
    expect(count(counters.commits, "section:Transform")).toBe(0);
  });

  it("keeps control items and their section cold across transient commits", () => {
    const schema = defineToolcraft({
      canvas: { enabled: true },
      panels: {
        controls: {
          sections: [
            {
              controls: {
                alpha: {
                  defaultValue: 10,
                  label: "Alpha",
                  max: 100,
                  min: 0,
                  target: "shape.alpha",
                  type: "slider",
                },
                beta: {
                  defaultValue: 20,
                  label: "Beta",
                  max: 100,
                  min: 0,
                  target: "shape.beta",
                  type: "slider",
                },
              },
              title: "Transform",
            },
          ],
          title: "Controls",
        },
        timeline: { mode: "playback" },
      },
    });
    const store = renderIsolationPanel(schema);

    act(() => {
      store.dispatchTransient({ currentTimeSeconds: 1, type: "timeline.setCurrentTime" });
      store.commitTransient("playback");
      store.dispatchTransient({
        offset: { x: 12, y: -8 },
        type: "canvas.setViewport",
        zoom: 125,
      });
      store.commitTransient("viewport");
    });

    expect(count(counters.executions, "alpha")).toBe(0);
    expect(count(counters.commits, "control:alpha")).toBe(0);
    expect(count(counters.executions, "beta")).toBe(0);
    expect(count(counters.commits, "control:beta")).toBe(0);
    expect(count(counters.commits, "section:Transform")).toBe(0);

    act(() => {
      store.dispatch({ target: "shape.alpha", type: "controls.setValue", value: 30 });
    });

    expect(count(counters.executions, "alpha")).toBe(1);
    expect(count(counters.commits, "control:alpha")).toBe(1);
    expect(count(counters.executions, "beta")).toBe(0);
    expect(count(counters.commits, "control:beta")).toBe(0);
    expect(count(counters.commits, "section:Transform")).toBe(0);
  });

  it("updates conditional controls only for their declared targets", () => {
    const schema = defineToolcraft({
      canvas: { enabled: true },
      panels: {
        controls: {
          sections: [
            {
              controls: {
                disabledAmount: {
                  defaultValue: 10,
                  disabledWhen: { equals: true, target: "mode.disabled" },
                  label: "Disabled amount",
                  max: 100,
                  min: 0,
                  target: "shape.disabledAmount",
                  type: "slider",
                },
                visibleAmount: {
                  defaultValue: 20,
                  label: "Visible amount",
                  max: 100,
                  min: 0,
                  target: "shape.visibleAmount",
                  type: "slider",
                  visibleWhen: { equals: true, target: "mode.visible" },
                },
              },
              title: "Conditional",
            },
          ],
          title: "Controls",
        },
      },
    });
    const store = renderIsolationPanel(schema, {
      values: { "mode.disabled": false, "mode.visible": false },
    });

    act(() => {
      store.dispatch({
        target: "unrelated.value",
        type: "controls.setValue",
        value: 1,
      });
    });

    expect(count(counters.executions, "disabledAmount")).toBe(0);
    expect(count(counters.executions, "visibleAmount")).toBe(0);
    expect(count(counters.commits, "section:Conditional")).toBe(0);

    act(() => {
      store.dispatch({
        target: "mode.disabled",
        type: "controls.setValue",
        value: true,
      });
    });

    expect(count(counters.executions, "disabledAmount")).toBe(1);
    expect(count(counters.commits, "control:disabledAmount")).toBe(1);
    expect(count(counters.executions, "visibleAmount")).toBe(0);
    expect(count(counters.commits, "section:Conditional")).toBe(0);

    resetCounters();
    act(() => {
      store.dispatch({
        target: "mode.visible",
        type: "controls.setValue",
        value: true,
      });
    });

    expect(count(counters.executions, "visibleAmount")).toBe(1);
    expect(count(counters.commits, "control:visibleAmount")).toBe(1);
    expect(count(counters.executions, "disabledAmount")).toBe(0);
    expect(count(counters.commits, "control:disabledAmount")).toBe(0);
    expect(count(counters.commits, "section:Conditional")).toBe(1);
  });

  it("isolates keyframe group and selection metadata by target", () => {
    const schema = defineToolcraft({
      canvas: { enabled: true },
      panels: {
        controls: {
          sections: [
            {
              controls: {
                alpha: {
                  defaultValue: 10,
                  label: "Alpha",
                  max: 100,
                  min: 0,
                  target: "shape.alpha",
                  type: "slider",
                },
                beta: {
                  defaultValue: 20,
                  label: "Beta",
                  max: 100,
                  min: 0,
                  target: "shape.beta",
                  type: "slider",
                },
              },
              title: "Animation",
            },
          ],
          title: "Controls",
        },
        timeline: true,
      },
    });
    const store = renderIsolationPanel(schema, {
      timeline: {
        expanded: true,
        keyframeGroups: [
          {
            controlId: "shape.alpha",
            keyframes: [
              {
                controlId: "shape.alpha",
                controlLabel: "Alpha",
                id: "alpha-0",
                timeSeconds: 0,
                value: 10,
                valueLabel: "10",
              },
            ],
            label: "Alpha",
          },
          {
            controlId: "shape.beta",
            keyframes: [
              {
                controlId: "shape.beta",
                controlLabel: "Beta",
                id: "beta-0",
                timeSeconds: 0,
                value: 20,
                valueLabel: "20",
              },
            ],
            label: "Beta",
          },
        ],
      },
    });

    act(() => {
      store.dispatch({ keyframeId: "alpha-0", type: "timeline.selectKeyframe" });
    });

    expect(count(counters.executions, "alpha")).toBe(1);
    expect(count(counters.commits, "control:alpha")).toBe(1);
    expect(count(counters.executions, "beta")).toBe(0);
    expect(count(counters.commits, "control:beta")).toBe(0);
    expect(count(counters.commits, "section:Animation")).toBe(0);

    resetCounters();
    act(() => {
      store.dispatch({ keyframeId: "beta-0", type: "timeline.selectKeyframe" });
    });
    resetCounters();
    act(() => {
      store.dispatch({
        easing: { type: "step" },
        keyframeId: "alpha-0",
        type: "timeline.changeKeyframeEasing",
      });
    });

    expect(count(counters.executions, "alpha")).toBe(1);
    expect(count(counters.commits, "control:alpha")).toBe(1);
    expect(count(counters.executions, "beta")).toBe(0);
    expect(count(counters.commits, "control:beta")).toBe(0);
    expect(count(counters.commits, "section:Animation")).toBe(0);
  });

  it("preserves isolation inside inline and compound render groups", () => {
    const schema = defineToolcraft({
      canvas: { enabled: true },
      panels: {
        controls: {
          sections: [
            {
              controls: {
                enabled: {
                  defaultValue: true,
                  label: "Enabled",
                  target: "shape.enabled",
                  type: "switch",
                },
                locked: {
                  defaultValue: false,
                  label: "Locked",
                  target: "shape.locked",
                  type: "switch",
                },
                fill: {
                  defaultValue: { hex: "#000000" },
                  label: "Fill",
                  semanticGroup: "shape-colors",
                  target: "shape.fill",
                  type: "color",
                },
                stroke: {
                  defaultValue: { hex: "#ffffff" },
                  label: "Stroke",
                  semanticGroup: "shape-colors",
                  target: "shape.stroke",
                  type: "color",
                },
              },
              layoutGroups: [
                { controls: ["enabled", "locked"], layout: "inline" },
              ],
              title: "Appearance",
            },
          ],
          title: "Controls",
        },
      },
    });
    const store = renderIsolationPanel(schema);

    act(() => {
      store.dispatch({
        target: "shape.enabled",
        type: "controls.setValue",
        value: false,
      });
    });

    expect(count(counters.executions, "enabled")).toBe(1);
    expect(count(counters.executions, "locked")).toBe(0);
    expect(count(counters.executions, "fill+stroke")).toBe(0);
    expect(count(counters.commits, "section:Appearance")).toBe(0);

    resetCounters();
    act(() => {
      store.dispatch({
        target: "shape.fill",
        type: "controls.setValue",
        value: { hex: "#ff0000" },
      });
    });

    expect(count(counters.executions, "fill+stroke")).toBe(1);
    expect(count(counters.commits, "control:fill+stroke")).toBe(1);
    expect(count(counters.executions, "enabled")).toBe(0);
    expect(count(counters.executions, "locked")).toBe(0);
    expect(count(counters.commits, "section:Appearance")).toBe(0);
  });
});
