import { describe, expect, test } from "bun:test";
import {
	getProjectId,
	transformProjectV1ToV2,
	type V1ToV2Context,
} from "../transformers/v1-to-v2";
import {
	projectWithNoId,
	projectWithNullValues,
	v1Project,
	v1ProjectWithMultipleScenes,
	v2Project,
} from "./fixtures";

const DEFAULT_FPS = 30;
const DEFAULT_BACKGROUND_BLUR_INTENSITY = 10;
const DEFAULT_BACKGROUND_COLOR = "#000000";
const DEFAULT_CANVAS_SIZE = { width: 1920, height: 1080 };

describe("V1 to V2 Migration", () => {
	describe("transformProjectV1ToV2", () => {
		test("creates metadata object from flat properties", () => {
			const result = transformProjectV1ToV2({ project: v1Project });

			expect(result.skipped).toBe(false);
			expect(result.project.version).toBe(2);

			const metadata = result.project.metadata as Record<string, unknown>;
			expect(metadata.id).toBe(v1Project.id);
			expect(metadata.name).toBe(v1Project.name);
			expect(typeof metadata.createdAt).toBe("string");
			expect(typeof metadata.updatedAt).toBe("string");
		});

		test("creates settings object from flat properties", () => {
			const result = transformProjectV1ToV2({ project: v1Project });

			const settings = result.project.settings as Record<string, unknown>;
			expect(settings.fps).toBe(v1Project.fps);
			expect(settings.canvasSize).toEqual(v1Project.canvasSize);
			expect(settings.originalCanvasSize).toBe(null);
		});

		test("converts color background correctly", () => {
			const result = transformProjectV1ToV2({ project: v1Project });

			const settings = result.project.settings as Record<string, unknown>;
			const background = settings.background as Record<string, unknown>;
			expect(background.type).toBe("color");
			expect(background.color).toBe(v1Project.backgroundColor);
		});

		test("converts blur background correctly", () => {
			const projectWithBlur = {
				...v1Project,
				backgroundType: "blur",
				blurIntensity: 30,
			};
			const result = transformProjectV1ToV2({ project: projectWithBlur });

			const settings = result.project.settings as Record<string, unknown>;
			const background = settings.background as Record<string, unknown>;
			expect(background.type).toBe("blur");
			expect(background.blurIntensity).toBe(30);
		});

		test("applies legacy bookmarks to main scene", () => {
			const result = transformProjectV1ToV2({ project: v1Project });

			const scenes = result.project.scenes as Array<Record<string, unknown>>;
			const mainScene = scenes.find((s) => s.isMain === true);
			expect(mainScene?.bookmarks).toEqual(v1Project.bookmarks);
		});

		test("preserves existing scene bookmarks", () => {
			const result = transformProjectV1ToV2({
				project: v1ProjectWithMultipleScenes,
			});

			const scenes = result.project.scenes as Array<Record<string, unknown>>;
			const introScene = scenes.find((s) => s.name === "Intro");
			expect(introScene?.bookmarks).toEqual([1.0]);
		});

		test("skips project that already has v2 structure", () => {
			const result = transformProjectV1ToV2({ project: v2Project });

			expect(result.skipped).toBe(true);
			expect(result.reason).toBe("already v2");
		});

		test("skips project with no id", () => {
			const result = transformProjectV1ToV2({ project: projectWithNoId });

			expect(result.skipped).toBe(true);
			expect(result.reason).toBe("no project id");
		});

		test("handles null values gracefully", () => {
			const result = transformProjectV1ToV2({
				project: projectWithNullValues,
			});

			expect(result.skipped).toBe(false);
			const settings = result.project.settings as Record<string, unknown>;
			expect(settings.fps).toBe(DEFAULT_FPS);
			expect(settings.canvasSize).toEqual(DEFAULT_CANVAS_SIZE);
		});

		test("uses default values for missing properties", () => {
			const minimalProject = {
				id: "minimal",
				version: 1,
				scenes: [],
			};
			const result = transformProjectV1ToV2({ project: minimalProject });

			const settings = result.project.settings as Record<string, unknown>;
			expect(settings.fps).toBe(DEFAULT_FPS);
			expect(settings.canvasSize).toEqual(DEFAULT_CANVAS_SIZE);

			const background = settings.background as Record<string, unknown>;
			expect(background.type).toBe("color");
			expect(background.color).toBe(DEFAULT_BACKGROUND_COLOR);
		});

		test("uses default blur intensity when missing", () => {
			const projectWithBlurNoIntensity = {
				id: "blur-no-intensity",
				version: 1,
				backgroundType: "blur",
				scenes: [],
			};
			const result = transformProjectV1ToV2({
				project: projectWithBlurNoIntensity,
			});

			const settings = result.project.settings as Record<string, unknown>;
			const background = settings.background as Record<string, unknown>;
			expect(background.blurIntensity).toBe(DEFAULT_BACKGROUND_BLUR_INTENSITY);
		});

		test("preserves currentSceneId", () => {
			const result = transformProjectV1ToV2({ project: v1Project });
			expect(result.project.currentSceneId).toBe(v1Project.currentSceneId);
		});

		test("finds main scene id when currentSceneId missing", () => {
			const projectWithoutCurrentScene = {
				...v1Project,
				currentSceneId: undefined,
			};
			const result = transformProjectV1ToV2({
				project: projectWithoutCurrentScene,
			});
			expect(result.project.currentSceneId).toBe("scene-main");
		});

		test("skips loading tracks if scene already has tracks", () => {
			const projectWithTracks = {
				...v1Project,
				scenes: [
					{
						id: "scene-main",
						name: "Main scene",
						isMain: true,
						tracks: [
							{
								id: "track-1",
								type: "video",
								name: "Existing Track",
								elements: [],
							},
						],
						bookmarks: [],
						createdAt: "2024-01-15T10:00:00.000Z",
						updatedAt: "2024-01-15T12:00:00.000Z",
					},
				],
			};

			const result = transformProjectV1ToV2({
				project: projectWithTracks,
			});

			const scenes = result.project.scenes as Array<Record<string, unknown>>;
			const mainScene = scenes[0];
			const tracks = mainScene.tracks as Array<Record<string, unknown>>;
			expect(tracks.length).toBe(1);
			expect(tracks[0].name).toBe("Existing Track");
		});
	});

	describe("Track Loading and Transformation", () => {
		test("loads tracks from legacy DB and transforms media track to video track", () => {
			const context: V1ToV2Context = {
				legacyTracksBySceneId: {
					"scene-main": [
						{
							id: "legacy-track-1",
							type: "media",
							name: "Legacy media track",
							elements: [
								{
									id: "media-element-1",
									name: "Test video clip",
									type: "media",
									mediaId: "media-1",
									duration: 120,
									startTime: 0,
									trimStart: 0,
									trimEnd: 0,
								},
							],
						},
					],
				},
				mediaTypesById: {
					"media-1": "video",
				},
			};

			const projectWithLegacyTracks = {
				...v1Project,
				scenes: [
					{
						id: "scene-main",
						name: "Main scene",
						isMain: true,
						tracks: [],
						bookmarks: [],
						createdAt: "2024-01-15T10:00:00.000Z",
						updatedAt: "2024-01-15T12:00:00.000Z",
					},
				],
			};

			const result = transformProjectV1ToV2({
				project: projectWithLegacyTracks,
				context,
			});

			const scenes = result.project.scenes as Array<Record<string, unknown>>;
			const mainScene = scenes[0];
			const tracks = mainScene.tracks as Array<Record<string, unknown>>;
			expect(Array.isArray(tracks)).toBe(true);
			expect(tracks).toHaveLength(1);
			expect(tracks[0].type).toBe("video");
			expect(tracks[0].isMain).toBe(true);
		});

		test("transforms text element preserving opacity and migrating position", () => {
			const context: V1ToV2Context = {
				legacyTracksBySceneId: {
					"scene-1": [
						{
							id: "legacy-text-track",
							type: "text",
							name: "Text",
							elements: [
								{
									id: "text-element-1",
									name: "Title",
									type: "text",
									content: "Hello",
									x: 120,
									y: 240,
									rotation: 15,
									opacity: 0.5,
									duration: 90,
									startTime: 10,
									trimStart: 0,
									trimEnd: 0,
								},
							],
						},
					],
				},
				mediaTypesById: {},
			};

			const projectWithTextTrack = {
				id: "project-text",
				version: 1,
				name: "Text Project",
				scenes: [
					{
						id: "scene-1",
						name: "Scene",
						isMain: true,
						tracks: [],
						bookmarks: [],
						createdAt: "2024-01-01T00:00:00.000Z",
						updatedAt: "2024-01-01T00:00:00.000Z",
					},
				],
			};

			const result = transformProjectV1ToV2({
				project: projectWithTextTrack,
				context,
			});

			expect(result.skipped).toBe(false);
			const scenes = result.project.scenes as Array<Record<string, unknown>>;
			const tracks = scenes[0].tracks as Array<Record<string, unknown>>;
			const elements = tracks[0].elements as Array<Record<string, unknown>>;
			const textElement = elements[0];
			expect(textElement.opacity).toBe(0.5);
			expect(textElement.transform).toEqual({
				scale: 1,
				position: { x: 120, y: 240 },
				rotate: 15,
			});
		});
	});

	describe("getProjectId", () => {
		test("returns id from root level", () => {
			const id = getProjectId({ project: v1Project });
			expect(id).toBe("project-v1-123");
		});

		test("returns id from metadata", () => {
			const id = getProjectId({ project: v1ProjectWithMultipleScenes });
			expect(id).toBe("project-v1-multi");
		});
	});
});
