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

import type { ToolcraftExternalStore } from "../../state/toolcraft-external-store";
import { ToolcraftRoot } from "./toolcraft-root";
import {
  CaptureStore,
  createHotPersistenceSchema,
  createValuesPersistenceSchema,
  StorePersistenceProbe,
} from "./toolcraft-root-persistence-test-utils";

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

describe("ToolcraftRoot committed and effective persistence", () => {
  it("does not restart persistence debounce for transient or non-included state", () => {
    vi.useFakeTimers();
    const schema = createValuesPersistenceSchema();

    render(
      <ToolcraftRoot schema={schema}>
        <StorePersistenceProbe />
      </ToolcraftRoot>,
    );

    act(() => {
      vi.advanceTimersByTime(100);
    });
    fireEvent.click(screen.getByRole("button", { name: "Scrub timeline" }));
    fireEvent.click(
      screen.getByRole("button", { name: "Expand non-persisted timeline" }),
    );
    act(() => {
      vi.advanceTimersByTime(20);
    });

    expect(
      JSON.parse(
        window.localStorage.getItem(
          "toolcraft:root-test-committed-values:state:v1",
        ) ?? "{}",
      ),
    ).toMatchObject({
      state: { values: { "selectedLayer.opacity": 75 } },
      version: 1,
    });

    window.localStorage.setItem(
      "toolcraft:root-test-committed-values:state:v1",
      "unchanged",
    );
    act(() => {
      vi.advanceTimersByTime(200);
    });

    expect(
      window.localStorage.getItem(
        "toolcraft:root-test-committed-values:state:v1",
      ),
    ).toBe("unchanged");
  });

  it("restarts persistence debounce for mandatory canvas changes", () => {
    vi.useFakeTimers();
    const schema = createValuesPersistenceSchema();

    render(
      <ToolcraftRoot schema={schema}>
        <StorePersistenceProbe />
      </ToolcraftRoot>,
    );

    act(() => {
      vi.advanceTimersByTime(120);
    });
    window.localStorage.setItem(
      "toolcraft:root-test-committed-values:state:v1",
      "unchanged",
    );

    fireEvent.click(screen.getByRole("button", { name: "Resize canvas" }));
    act(() => {
      vi.advanceTimersByTime(119);
    });

    expect(
      window.localStorage.getItem(
        "toolcraft:root-test-committed-values:state:v1",
      ),
    ).toBe("unchanged");

    act(() => {
      vi.advanceTimersByTime(1);
    });

    expect(
      JSON.parse(
        window.localStorage.getItem(
          "toolcraft:root-test-committed-values:state:v1",
        ) ?? "{}",
      ),
    ).toMatchObject({
      state: {
        canvas: {
          size: { height: 480, unit: "px", width: 640 },
        },
      },
      version: 1,
    });
  });

  it("schedules a persisted hot slice only when its transient lane commits", () => {
    vi.useFakeTimers();
    const schema = createHotPersistenceSchema();

    render(
      <ToolcraftRoot schema={schema}>
        <StorePersistenceProbe />
      </ToolcraftRoot>,
    );

    act(() => {
      vi.advanceTimersByTime(120);
    });
    window.localStorage.setItem(
      "toolcraft:root-test-hot:state:v1",
      "unchanged",
    );

    fireEvent.click(screen.getByRole("button", { name: "Scrub timeline" }));
    act(() => {
      vi.advanceTimersByTime(200);
    });

    expect(
      window.localStorage.getItem("toolcraft:root-test-hot:state:v1"),
    ).toBe("unchanged");

    fireEvent.click(screen.getByRole("button", { name: "Commit playback" }));
    act(() => {
      vi.advanceTimersByTime(119);
    });

    expect(
      window.localStorage.getItem("toolcraft:root-test-hot:state:v1"),
    ).toBe("unchanged");

    act(() => {
      vi.advanceTimersByTime(1);
    });

    expect(
      JSON.parse(
        window.localStorage.getItem("toolcraft:root-test-hot:state:v1") ??
          "{}",
      ),
    ).toMatchObject({
      state: { timeline: { currentTimeSeconds: 2 } },
      version: 1,
    });
  });

  it("debounces committed state instead of transient overlays", () => {
    vi.useFakeTimers();
    const schema = createHotPersistenceSchema();

    render(
      <ToolcraftRoot schema={schema}>
        <StorePersistenceProbe />
      </ToolcraftRoot>,
    );

    act(() => {
      vi.advanceTimersByTime(120);
    });
    fireEvent.click(screen.getByRole("button", { name: "Scrub timeline" }));
    fireEvent.click(screen.getByRole("button", { name: "Move viewport" }));
    fireEvent.click(
      screen.getByRole("button", { name: "Set persisted value" }),
    );
    act(() => {
      vi.advanceTimersByTime(120);
    });

    expect(
      JSON.parse(
        window.localStorage.getItem("toolcraft:root-test-hot:state:v1") ??
          "{}",
      ),
    ).toMatchObject({
      state: {
        canvas: { offset: { x: 0, y: 0 }, zoom: 100 },
        timeline: { currentTimeSeconds: 0 },
        values: { "selectedLayer.opacity": 50 },
      },
      version: 1,
    });
  });

  it("flushes committed state on pagehide without transient overlays", () => {
    vi.useFakeTimers();
    const schema = createHotPersistenceSchema();

    render(
      <ToolcraftRoot schema={schema}>
        <StorePersistenceProbe />
      </ToolcraftRoot>,
    );

    fireEvent.click(screen.getByRole("button", { name: "Scrub timeline" }));
    fireEvent.click(screen.getByRole("button", { name: "Move viewport" }));
    fireEvent.click(
      screen.getByRole("button", { name: "Set persisted value" }),
    );
    window.dispatchEvent(new Event("pagehide"));

    expect(
      JSON.parse(
        window.localStorage.getItem("toolcraft:root-test-hot:state:v1") ??
          "{}",
      ),
    ).toMatchObject({
      state: {
        canvas: { offset: { x: 0, y: 0 }, zoom: 100 },
        timeline: { currentTimeSeconds: 0 },
        values: { "selectedLayer.opacity": 50 },
      },
      version: 1,
    });

    act(() => {
      vi.advanceTimersByTime(200);
    });

    expect(
      JSON.parse(
        window.localStorage.getItem("toolcraft:root-test-hot:state:v1") ??
          "{}",
      ),
    ).toMatchObject({
      state: {
        canvas: { offset: { x: 0, y: 0 }, zoom: 100 },
        timeline: { currentTimeSeconds: 0 },
        values: { "selectedLayer.opacity": 50 },
      },
      version: 1,
    });
  });

  it("keeps transient hot fields out of persisted keyframe history roundtrips", () => {
    vi.useFakeTimers();
    const schema = createHotPersistenceSchema();

    render(
      <ToolcraftRoot schema={schema}>
        <StorePersistenceProbe />
      </ToolcraftRoot>,
    );

    act(() => {
      vi.advanceTimersByTime(120);
    });
    fireEvent.click(screen.getByRole("button", { name: "Scrub timeline" }));
    fireEvent.click(screen.getByRole("button", { name: "Move viewport" }));
    fireEvent.click(screen.getByRole("button", { name: "Set keyframe" }));
    fireEvent.click(screen.getByRole("button", { name: "Undo" }));
    fireEvent.click(screen.getByRole("button", { name: "Redo" }));

    act(() => {
      vi.advanceTimersByTime(120);
    });

    expect(
      JSON.parse(
        window.localStorage.getItem("toolcraft:root-test-hot:state:v1") ??
          "{}",
      ),
    ).toMatchObject({
      state: {
        canvas: { offset: { x: 0, y: 0 }, zoom: 100 },
        timeline: {
          currentTimeSeconds: 0,
          keyframeGroups: [
            {
              keyframes: [{ id: "selectedLayer.opacity::2" }],
            },
          ],
        },
      },
      version: 1,
    });
  });

  it("flushes pending committed state and cleans up subscriptions on unmount", () => {
    vi.useFakeTimers();
    let store: ToolcraftExternalStore | undefined;
    const schema = createHotPersistenceSchema();
    const view = render(
      <ToolcraftRoot schema={schema}>
        <CaptureStore capture={(value) => { store = value; }} />
      </ToolcraftRoot>,
    );

    act(() => {
      vi.advanceTimersByTime(120);
    });
    window.localStorage.setItem(
      "toolcraft:root-test-hot:state:v1",
      "unchanged",
    );

    act(() => {
      store?.dispatch({
        target: "selectedLayer.opacity",
        type: "controls.setValue",
        value: 50,
      });
      view.unmount();
    });

    expect(
      JSON.parse(
        window.localStorage.getItem("toolcraft:root-test-hot:state:v1") ??
          "{}",
      ),
    ).toMatchObject({
      state: { values: { "selectedLayer.opacity": 50 } },
      version: 1,
    });

    act(() => {
      store?.dispatch({
        target: "selectedLayer.opacity",
        type: "controls.setValue",
        value: 25,
      });
      vi.advanceTimersByTime(200);
    });

    expect(
      JSON.parse(
        window.localStorage.getItem("toolcraft:root-test-hot:state:v1") ??
          "{}",
      ).state.values["selectedLayer.opacity"],
    ).toBe(50);
  });
});
