import "@testing-library/jest-dom/vitest";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";

// The store is a module singleton constructed on first import: it reads
// localStorage and `matchMedia` in its constructor. Each test therefore
// stubs `matchMedia` and resets the module registry, then imports a
// freshly-constructed store via `freshStore`.

const STORAGE_KEY = "galaxy-theme";

function stubMatchMedia(prefersLight: boolean): void {
	Object.defineProperty(window, "matchMedia", {
		writable: true,
		configurable: true,
		value: (query: string) => ({
			matches: query.includes("light") ? prefersLight : false,
			media: query,
			onchange: null,
			addListener: () => {},
			removeListener: () => {},
			addEventListener: () => {},
			removeEventListener: () => {},
			dispatchEvent: () => false,
		}),
	});
}

async function freshStore(
	prefersLight = false,
): Promise<typeof import("../src/lib/theme/theme.svelte")> {
	stubMatchMedia(prefersLight);
	vi.resetModules();
	return import("../src/lib/theme/theme.svelte");
}

describe("theme store", () => {
	beforeEach(() => {
		localStorage.clear();
		delete document.documentElement.dataset.theme;
	});
	afterEach(() => {
		vi.restoreAllMocks();
	});

	it("defaults to system and applies the resolved theme", async () => {
		const { theme } = await freshStore();
		expect(theme.choice).toBe("system");
		// freshStore() leaves the OS at dark (prefersLight = false).
		expect(theme.resolved).toBe("dark");
		expect(document.documentElement.dataset.theme).toBe("dark");
	});

	it("persists an explicit choice and writes data-theme", async () => {
		const { theme, THEME_STORAGE_KEY } = await freshStore();
		expect(THEME_STORAGE_KEY).toBe(STORAGE_KEY);

		theme.setChoice("light");
		expect(theme.choice).toBe("light");
		expect(theme.resolved).toBe("light");
		expect(document.documentElement.dataset.theme).toBe("light");
		expect(localStorage.getItem(STORAGE_KEY)).toBe("light");

		theme.setChoice("dark");
		expect(theme.resolved).toBe("dark");
		expect(document.documentElement.dataset.theme).toBe("dark");
		expect(localStorage.getItem(STORAGE_KEY)).toBe("dark");
	});

	it("reads the stored choice on construction", async () => {
		localStorage.setItem(STORAGE_KEY, "light");
		const { theme } = await freshStore();
		expect(theme.choice).toBe("light");
		expect(theme.resolved).toBe("light");
	});

	it("resolves system to the OS preference", async () => {
		const { theme } = await freshStore(true);
		theme.setChoice("system");
		expect(theme.choice).toBe("system");
		expect(theme.resolved).toBe("light");
		expect(document.documentElement.dataset.theme).toBe("light");
	});

	it("falls back to system for an unrecognised stored value", async () => {
		localStorage.setItem(STORAGE_KEY, "neon");
		const { theme } = await freshStore();
		expect(theme.choice).toBe("system");
	});

	it("applies an ephemeral override without touching the persisted choice", async () => {
		localStorage.setItem(STORAGE_KEY, "dark");
		const { theme } = await freshStore();
		expect(theme.resolved).toBe("dark");
		expect(theme.override).toBeNull();

		theme.setOverride("light");
		expect(theme.override).toBe("light");
		expect(theme.resolved).toBe("light");
		expect(document.documentElement.dataset.theme).toBe("light");
		// Persisted choice is untouched.
		expect(theme.choice).toBe("dark");
		expect(localStorage.getItem(STORAGE_KEY)).toBe("dark");
	});

	it("clearOverride re-projects the persisted choice", async () => {
		localStorage.setItem(STORAGE_KEY, "light");
		const { theme } = await freshStore();
		theme.setOverride("dark");
		expect(theme.resolved).toBe("dark");

		theme.clearOverride();
		expect(theme.override).toBeNull();
		expect(theme.resolved).toBe("light");
		expect(document.documentElement.dataset.theme).toBe("light");
	});

	it("override shadows setChoice until cleared", async () => {
		const { theme } = await freshStore();
		theme.setOverride("light");
		theme.setChoice("dark");
		// Override wins while it is non-null, but the choice is still
		// persisted for the next lobby visit.
		expect(theme.resolved).toBe("light");
		expect(theme.choice).toBe("dark");
		expect(localStorage.getItem(STORAGE_KEY)).toBe("dark");

		theme.clearOverride();
		expect(theme.resolved).toBe("dark");
	});
});
