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

import { defineToolcraft } from "../../schema/define-toolcraft";
import {
  getLegacyToolcraftSectionCollapseEntryKey,
  getLegacyToolcraftSectionCollapseStorageKey,
  readLegacyToolcraftSectionCollapseMigration,
} from "../../state/persistence-legacy-section-collapse";
import type { ToolcraftExternalStore } from "../../state/toolcraft-external-store";
import { ToolcraftRoot } from "./toolcraft-root";
import { CaptureStore } from "./toolcraft-root-persistence-test-utils";
import { useToolcraftPersistenceStatus } from "./use-toolcraft-persistence";

function PersistenceStatusProbe() {
  const status = useToolcraftPersistenceStatus();

  return <span data-testid="persistence-status">{JSON.stringify(status)}</span>;
}

function requireCapturedStore(
  store: ToolcraftExternalStore | undefined,
): ToolcraftExternalStore {
  if (!store) {
    throw new Error("Expected ToolcraftRoot to publish its external store.");
  }
  return store;
}

function createMigrationPersistenceSchema() {
  return defineToolcraft({
    canvas: { enabled: true },
    identity: { id: "root-migration-test", title: "Root Migration Test" },
    panels: {
      controls: {
        sections: [
          {
            controls: {
              opacity: {
                defaultValue: 75,
                target: "selectedLayer.opacity",
                type: "slider",
              },
            },
            id: "appearance",
            title: "Appearance",
          },
        ],
        title: "Controls",
      },
    },
  });
}

function createMediaMigrationPersistenceSchema() {
  return defineToolcraft({
    canvas: { enabled: true },
    identity: {
      id: "root-media-migration-test",
      title: "Root Media Migration Test",
    },
    panels: {
      controls: {
        sections: [
          {
            controls: {
              source: {
                defaultValue: null,
                target: "source.image",
                type: "fileDrop",
              },
            },
          },
        ],
        title: "Controls",
      },
    },
  });
}

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

describe("ToolcraftRoot persistence migration", () => {
  it("publishes v2 only after legacy media bytes become durable", async () => {
    vi.useFakeTimers();
    const schema = createMediaMigrationPersistenceSchema();

    if (schema.persistence.storage !== "localStorage") {
      throw new Error("Expected local persistence");
    }

    const legacySnapshot = JSON.stringify({
      state: {
        mediaAssets: [
          {
            dataUrl: "data:image/png;base64,AQID",
            fileName: "legacy.png",
            id: "legacy-image",
            layerId: "legacy-layer",
            mimeType: "image/png",
            position: { x: 0, y: 0 },
            size: { height: 1, unit: "px", width: 1 },
          },
        ],
      },
      version: 1,
    });
    window.localStorage.setItem(schema.persistence.key, legacySnapshot);
    let store: ToolcraftExternalStore | undefined;

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

    act(() => {
      vi.advanceTimersByTime(120);
    });
    expect(window.localStorage.getItem(schema.persistence.key)).toBe(
      legacySnapshot,
    );

    await vi.waitFor(() => {
      expect(requireCapturedStore(store).getState().mediaAssets[0]).toMatchObject({
        lifecycle: "ready",
      });
    });
    act(() => {
      vi.advanceTimersByTime(120);
    });

    const migratedSnapshot = window.localStorage.getItem(
      schema.persistence.key,
    );
    expect(migratedSnapshot).not.toContain("data:image/");
    expect(JSON.parse(migratedSnapshot ?? "{}").version).toBe(2);
  });

  it("preserves legacy media bytes when repository migration fails", async () => {
    vi.useFakeTimers();
    const schema = createMediaMigrationPersistenceSchema();

    if (schema.persistence.storage !== "localStorage") {
      throw new Error("Expected local persistence");
    }

    const legacySnapshot = JSON.stringify({
      state: {
        mediaAssets: [
          {
            dataUrl: "invalid-data-url",
            fileName: "legacy.png",
            id: "legacy-image",
            layerId: "legacy-layer",
            mimeType: "image/png",
            position: { x: 0, y: 0 },
          },
        ],
      },
      version: 1,
    });
    window.localStorage.setItem(schema.persistence.key, legacySnapshot);
    let store: ToolcraftExternalStore | undefined;

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

    await vi.waitFor(() => {
      expect(requireCapturedStore(store).getState().mediaAssets[0]).toMatchObject({
        lifecycle: "unavailable",
      });
    });
    act(() => {
      window.dispatchEvent(new Event("pagehide"));
      vi.advanceTimersByTime(120);
    });

    expect(screen.getByTestId("persistence-status").textContent).toBe(
      JSON.stringify({ reason: "unavailable", status: "failed" }),
    );
    expect(window.localStorage.getItem(schema.persistence.key)).toBe(
      legacySnapshot,
    );
  });

  it("preserves a newer unsupported snapshot and reports incompatibility", () => {
    vi.useFakeTimers();
    const schema = createMigrationPersistenceSchema();

    if (schema.persistence.storage !== "localStorage") {
      throw new Error("Expected local persistence");
    }

    const newerSnapshot = JSON.stringify({
      state: { values: { "selectedLayer.opacity": 50 } },
      version: schema.persistence.version + 1,
    });
    window.localStorage.setItem(schema.persistence.key, newerSnapshot);

    render(
      <ToolcraftRoot schema={schema}>
        <PersistenceStatusProbe />
      </ToolcraftRoot>,
    );
    act(() => {
      vi.advanceTimersByTime(500);
    });

    expect(screen.getByTestId("persistence-status").textContent).toBe(
      JSON.stringify({
        reason: "incompatible-version",
        status: "failed",
      }),
    );
    expect(window.localStorage.getItem(schema.persistence.key)).toBe(
      newerSnapshot,
    );
  });

  it("removes legacy collapse data only after expanded state is persisted", () => {
    vi.useFakeTimers();
    const schema = createMigrationPersistenceSchema();

    if (schema.persistence.storage !== "localStorage") {
      throw new Error("Expected local persistence");
    }

    const sections = schema.panels.controls?.sections ?? [];
    const sectionIndex = sections.findIndex(
      (candidate) => candidate.id === "appearance",
    );
    const section = sections[sectionIndex];
    const legacyKey = getLegacyToolcraftSectionCollapseStorageKey(schema);
    if (!section || !legacyKey) {
      throw new Error("Expected the migration section and legacy storage key.");
    }
    window.localStorage.setItem(
      legacyKey,
      JSON.stringify({
        collapsed: [
          getLegacyToolcraftSectionCollapseEntryKey(section, sectionIndex),
        ],
        version: 1,
      }),
    );
    expect(readLegacyToolcraftSectionCollapseMigration(schema)).toEqual({
      panels: {
        controls: { collapsedSections: { appearance: true } },
      },
      storageKey: legacyKey,
    });
    let store: ToolcraftExternalStore | undefined;
    const first = render(
      <ToolcraftRoot schema={schema}>
        <CaptureStore capture={(value) => { store = value; }} />
      </ToolcraftRoot>,
    );

    expect(
      requireCapturedStore(store).getState().panels.controls.collapsedSections.appearance,
    ).toBe(true);
    expect(window.localStorage.getItem(legacyKey)).not.toBeNull();

    act(() => {
      requireCapturedStore(store).dispatch({
        collapsed: false,
        sectionId: "appearance",
        type: "panels.setSectionCollapsed",
      });
      vi.advanceTimersByTime(120);
    });

    expect(window.localStorage.getItem(legacyKey)).toBeNull();
    first.unmount();
    let remountedStore: ToolcraftExternalStore | undefined;

    render(
      <ToolcraftRoot schema={schema}>
        <CaptureStore capture={(value) => { remountedStore = value; }} />
      </ToolcraftRoot>,
    );

    expect(
      requireCapturedStore(remountedStore).getState().panels.controls
        .collapsedSections.appearance,
    ).toBeUndefined();
  });
});
