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 { isToolcraftControlsResetHistoryPatch } from "../../../state/history-patch-metadata";
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(() => ({
  resetGenerationScans: 0,
  sectionCommits: 0,
  shellSelectorExecutions: 0,
}));

function CommitProbe(): null {
  React.useLayoutEffect(() => {
    counters.sectionCommits += 1;
  });

  return null;
}

vi.mock("../../../state/toolcraft-external-store-dependencies", async (importOriginal) => {
  const actual = await importOriginal<
    typeof import("../../../state/toolcraft-external-store-dependencies")
  >();

  return {
    ...actual,
    getToolcraftHistorySourceGeneration: (
      ...args: Parameters<typeof actual.getToolcraftHistorySourceGeneration>
    ): number => {
      counters.resetGenerationScans += 1;
      return actual.getToolcraftHistorySourceGeneration(...args);
    },
  };
});

vi.mock("../conditions/control-conditions", async (importOriginal) => {
  const actual = await importOriginal<
    typeof import("../conditions/control-conditions")
  >();

  return {
    ...actual,
    isToolcraftSectionVisible: (
      ...args: Parameters<typeof actual.isToolcraftSectionVisible>
    ): boolean => {
      if (args[1].title === "Transform") {
        counters.shellSelectorExecutions += 1;
      }

      return actual.isToolcraftSectionVisible(...args);
    },
  };
});

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

  return {
    ...actual,
    PanelSection: (props: React.ComponentProps<typeof actual.PanelSection>) => (
      <>
        {props.title === "Transform" ? <CommitProbe /> : null}
        <actual.PanelSection {...props} />
      </>
    ),
  };
});

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

function resetCounters(): void {
  counters.resetGenerationScans = 0;
  counters.sectionCommits = 0;
  counters.shellSelectorExecutions = 0;
}

function renderPanel(): {
  store: ToolcraftExternalStore;
  target: string;
} {
  const schema = defineToolcraft({
    canvas: { enabled: false },
    panels: {
      controls: {
        sections: [
          {
            controls: {
              amount: {
                defaultValue: 75,
                label: "Amount",
                max: 100,
                min: 0,
                target: "effect.amount",
                type: "slider",
              },
            },
            title: "Transform",
          },
        ],
        title: "Controls",
      },
    },
  });
  const target = schema.panels.controls?.sections.find(
    (section) => section.controls.amount !== undefined,
  )?.controls.amount?.target;
  let store: ToolcraftExternalStore | undefined;

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

  if (!store || !target) {
    throw new Error("Expected the controls panel store and amount target.");
  }

  resetCounters();
  return { store, target };
}

function expectNoShellWork(): void {
  expect(counters.shellSelectorExecutions).toBe(0);
  expect(counters.resetGenerationScans).toBe(0);
  expect(counters.sectionCommits).toBe(0);
}

function expectOneResetGeneration(): void {
  expect(counters.shellSelectorExecutions).toBe(1);
  expect(counters.resetGenerationScans).toBe(1);
  expect(counters.sectionCommits).toBe(1);
}

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

describe("ControlsPanel reset dependency isolation", () => {
  it("keeps shell selection cold across growing ordinary history", () => {
    const { store, target } = renderPanel();

    for (let value = 1; value <= 40; value += 1) {
      act(() => {
        store.dispatch({ target, type: "controls.setValue", value });
      });
    }

    expect(store.getState().history.undo).toHaveLength(40);
    expectNoShellWork();
  });

  it("executes once for reset, undo, and redo generation changes", () => {
    const { store, target } = renderPanel();

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

    act(() => {
      store.dispatch({ type: "controls.reset" });
    });

    expectOneResetGeneration();
    expect(
      store.getState().history.undo.filter(
        isToolcraftControlsResetHistoryPatch,
      ),
    ).toHaveLength(1);
    resetCounters();

    act(() => {
      store.dispatch({ target, type: "controls.setValue", value: 41 });
    });
    act(() => {
      store.dispatch({ type: "history.undo" });
    });

    expectNoShellWork();
    resetCounters();

    act(() => {
      store.dispatch({ type: "history.undo" });
    });

    expect(store.getState().values[target]).toBe(40);
    expectOneResetGeneration();
    resetCounters();

    act(() => {
      store.dispatch({ type: "history.redo" });
    });

    expect(store.getState().values[target]).toBe(75);
    expectOneResetGeneration();
    resetCounters();

    act(() => {
      store.dispatch({ type: "history.undo" });
    });
    resetCounters();
    act(() => {
      store.dispatch({ target, type: "controls.setValue", value: 42 });
    });

    expect(store.getState().history.redo).toEqual([]);
    expectNoShellWork();
  });
});
