<script module lang="ts">
	import { defineMeta, type StoryContext } from "@storybook/addon-svelte-csf";
	import { expect, waitFor } from "storybook/test";
	import type { DiffFile } from "$lib/api/diff";
	import DiffView from "./DiffView.svelte";

	const textFile: DiffFile = {
		name: { old: "src/colors.ts", new: "src/colors.ts" },
		text_fragments: [
			{
				OldPosition: 1,
				OldLines: 3,
				NewPosition: 1,
				NewLines: 4,
				LinesAdded: 2,
				LinesDeleted: 1,
				Lines: [
					{ Op: 0, Line: "export const palette = {\n" },
					{ Op: 1, Line: '\tbackground: "white",\n' },
					{ Op: 2, Line: '\tbackground: "oklch(0.98 0 0)",\n' },
					{ Op: 2, Line: '\tforeground: "oklch(0.2 0 0)",\n' },
					{ Op: 0, Line: "};\n" }
				]
			}
		]
	};

	const binaryFile: DiffFile = {
		name: { old: "assets/logo.png", new: "assets/logo.png" },
		is_binary: true
	};

	const downloadUrls = {
		patch: "/dawn/tangled/commit/0123456789abcdef0123456789abcdef01234567.patch",
		diff: "/dawn/tangled/commit/0123456789abcdef0123456789abcdef01234567.diff"
	};

	const blobBase = "/dawn/tangled/blob/0123456789abcdef0123456789abcdef01234567";

	type PlayContext = Pick<
		StoryContext<Record<string, unknown>>,
		"canvas" | "canvasElement" | "userEvent"
	>;

	// pierre renders inside a shadow root, diff content is invisible to canvas
	const shadowText = (canvasElement: HTMLElement) =>
		[...canvasElement.querySelectorAll("diffs-container")]
			.map((host) => host.shadowRoot?.textContent ?? "")
			.join("\n");

	const hasSplitColumns = (canvasElement: HTMLElement) =>
		[...canvasElement.querySelectorAll("diffs-container")].some(
			(host) =>
				host.shadowRoot?.querySelector("[data-deletions]") &&
				host.shadowRoot?.querySelector("[data-additions]")
		);

	// the per-file cards, excluding the file tree's directory <details>
	const fileCards = (canvasElement: HTMLElement) => [
		...canvasElement.querySelectorAll<HTMLDetailsElement>("details[id^='file-']")
	];

	const mixedFiles = async ({ canvas, canvasElement, userEvent }: PlayContext) => {
		await expect(canvas.getAllByText("+2")).toHaveLength(2);
		await expect(canvas.getAllByText("-1")).toHaveLength(2);
		await expect(canvas.getByText("2 changed files")).toBeVisible();

		await expect(canvas.getByText("src/colors.ts")).toBeVisible();
		await waitFor(() => expect(shadowText(canvasElement)).toContain("palette"));
		expect(shadowText(canvasElement)).not.toContain("src/colors.ts");
		expect(hasSplitColumns(canvasElement)).toBe(false);

		await expect(canvas.getByText("assets/logo.png")).toBeVisible();
		await expect(
			canvas.getByText("This is a binary file and will not be displayed.")
		).toBeVisible();
		expect(shadowText(canvasElement)).not.toContain("assets/logo.png");

		await expect(canvas.getByRole("link", { name: "colors.ts" })).toHaveAttribute(
			"href",
			"#file-src/colors.ts"
		);

		await expect(canvas.getByRole("link", { name: ".patch" })).toHaveAttribute(
			"href",
			downloadUrls.patch
		);
		await expect(canvas.getByRole("link", { name: "Split diff" })).toHaveAttribute(
			"href",
			"?diff=split"
		);
		await expect(canvas.getByRole("link", { name: "Unified diff" })).toHaveAttribute(
			"href",
			"?diff=unified"
		);

		expect(fileCards(canvasElement).every((card) => card.open)).toBe(true);
		const firstCard = fileCards(canvasElement)[0];
		await userEvent.click(firstCard.querySelector("summary")!);
		expect(firstCard.open).toBe(false);
		expect(fileCards(canvasElement)[1].open).toBe(true);

		await userEvent.click(canvas.getByRole("button", { name: "Expand all" }));
		expect(fileCards(canvasElement).every((card) => card.open)).toBe(true);
		await userEvent.click(canvas.getByRole("button", { name: "Collapse all" }));
		expect(fileCards(canvasElement).every((card) => !card.open)).toBe(true);
	};

	const splitView = async ({ canvasElement }: PlayContext) => {
		await waitFor(() => expect(shadowText(canvasElement)).toContain("palette"));
		await waitFor(() => expect(hasSplitColumns(canvasElement)).toBe(true));
	};

	const viewFileLinks = async ({ canvas }: PlayContext) => {
		const links = canvas.getAllByRole("link", { name: "View file" });
		await expect(links).toHaveLength(2);
		await expect(links[0]).toHaveAttribute("href", `${blobBase}/src/colors.ts`);
		await expect(links[1]).toHaveAttribute("href", `${blobBase}/assets/logo.png`);
	};

	const { Story } = defineMeta({
		title: "Repo/DiffView",
		component: DiffView,
		tags: ["autodocs"],
		args: {
			files: [textFile, binaryFile],
			stat: { insertions: 2, deletions: 1, files_changed: 2 },
			downloadUrls
		}
	});
</script>

<Story name="Mixed text and binary" play={mixedFiles} />

<Story name="Split" args={{ diffStyle: "split" }} play={splitView} />

<Story name="With blob base" args={{ blobBase }} play={viewFileLinks} />

<Story name="No downloads" args={{ downloadUrls: undefined }} />
