import {
	test,
	expect,
	MOCK_SEGMENTS,
	MOCK_DURATION_MS,
	MOCK_TOTAL_REMOVED_MS,
} from "#tests/e2e/fixtures/fragments"

test.describe("Fragments Step (Integration)", () => {
	test.describe("Initial State", () => {
		test("should display the fragments step", async ({
			fragmentsPage,
		}) => {
			const { fragmentsStep } = fragmentsPage

			await expect(fragmentsStep).toBeVisible()
		})

		test("should display the correct fragment count", async ({
			fragmentsPage,
		}) => {
			const { fragmentsStep } = fragmentsPage

			await expect(
				fragmentsStep.getByText(`Фрагментов: ${MOCK_SEGMENTS.length}`),
			).toBeVisible()
		})

		test("should display the correct total removal duration", async ({
			fragmentsPage,
		}) => {
			const { fragmentsStep } = fragmentsPage

			const totalSec = Math.floor(MOCK_TOTAL_REMOVED_MS / 1000)
			await expect(
				fragmentsStep.getByText(`Будет удалено: ${totalSec}с`),
			).toBeVisible()
		})

		test("should display the video player", async ({ fragmentsPage }) => {
			const { fragmentsStep } = fragmentsPage

			await expect(
				fragmentsStep.locator("media-player"),
			).toBeVisible({ timeout: 10_000 })
		})

		test("should display zoom controls", async ({ fragmentsPage }) => {
			const { fragmentsStep } = fragmentsPage

			await expect(fragmentsStep.getByText("Масштаб")).toBeVisible()
			await expect(
				fragmentsStep.locator("button").filter({ hasText: "-" }),
			).toBeVisible()
			await expect(
				fragmentsStep.locator("button").filter({ hasText: "+" }),
			).toBeVisible()
		})

		test("should display the apply button when regions exist", async ({
			fragmentsPage,
		}) => {
			const { fragmentsStep } = fragmentsPage

			await expect(
				fragmentsStep.getByRole("button", { name: "Применить" }),
			).toBeVisible()
		})

		test("should display the cancel button", async ({
			fragmentsPage,
		}) => {
			const { fragmentsStep } = fragmentsPage

			const cancelButton = fragmentsStep.getByRole("button", {
				name: "Отмена",
			})
			await expect(cancelButton).toBeVisible()
			await expect(cancelButton).toBeEnabled()
		})
	})

	test.describe("Zoom Controls", () => {
		test("should increase timeline width when + is clicked", async ({
			fragmentsPage,
		}) => {
			const { fragmentsStep } = fragmentsPage

			const timelineInner = fragmentsStep
				.locator("[class*='timelineInner']")
				.first()

			// Wait for initial render
			await timelineInner.waitFor({ timeout: 5_000 })
			const initialWidth = await timelineInner.evaluate(
				(el) => el.clientWidth,
			)

			// Click zoom in multiple times
			const zoomIn = fragmentsStep
				.locator("button")
				.filter({ hasText: "+" })
			await zoomIn.click()
			await zoomIn.click()
			await zoomIn.click()

			const newWidth = await timelineInner.evaluate(
				(el) => el.clientWidth,
			)
			expect(newWidth).toBeGreaterThan(initialWidth)
		})

		test("should decrease timeline width when - is clicked", async ({
			fragmentsPage,
		}) => {
			const { fragmentsStep } = fragmentsPage

			const timelineInner = fragmentsStep
				.locator("[class*='timelineInner']")
				.first()

			// Zoom in first to have room to zoom out
			const zoomIn = fragmentsStep
				.locator("button")
				.filter({ hasText: "+" })
			await zoomIn.click()
			await zoomIn.click()
			await zoomIn.click()

			await timelineInner.waitFor({ timeout: 5_000 })
			const widthAfterZoomIn = await timelineInner.evaluate(
				(el) => el.clientWidth,
			)

			// Zoom out
			const zoomOut = fragmentsStep
				.locator("button")
				.filter({ hasText: "-" })
			await zoomOut.click()
			await zoomOut.click()

			const widthAfterZoomOut = await timelineInner.evaluate(
				(el) => el.clientWidth,
			)
			expect(widthAfterZoomOut).toBeLessThan(widthAfterZoomIn)
		})
	})

	test.describe("Context Menu — Add Region", () => {
		test("should show context menu with add option on right-click on timeline", async ({
			fragmentsPage,
		}) => {
			const { fragmentsStep } = fragmentsPage

			const timeline = fragmentsStep
				.locator("[class*='timelineContainer']")
				.first()
			await timeline.click({ button: "right", position: { x: 50, y: 50 } })

			await expect(
				fragmentsStep.getByText("Добавить новый"),
			).toBeVisible()
		})

		test("should add a new region via context menu", async ({
			fragmentsPage,
		}) => {
			const { fragmentsStep } = fragmentsPage

			const timeline = fragmentsStep
				.locator("[class*='timelineContainer']")
				.first()
			await timeline.click({ button: "right", position: { x: 50, y: 50 } })

			await fragmentsStep.getByText("Добавить новый").click()

			await expect(
				fragmentsStep.getByText(
					`Фрагментов: ${MOCK_SEGMENTS.length + 1}`,
				),
			).toBeVisible()
		})
	})

	test.describe("Context Menu — Delete Region", () => {
		test("should show delete and add options when right-clicking a cut region", async ({
			fragmentsPage,
		}) => {
			const { fragmentsStep } = fragmentsPage

			const region = fragmentsStep
				.locator("[data-testid='cut-region']")
				.first()
			await region.waitFor({ timeout: 5_000 })
			await region.click({ button: "right", force: true })

			await expect(
				fragmentsStep.getByText("Удалить"),
			).toBeVisible()
			await expect(
				fragmentsStep.getByText("Добавить новый"),
			).toBeVisible()
		})

		test("should delete a region via context menu", async ({
			fragmentsPage,
		}) => {
			const { fragmentsStep } = fragmentsPage

			const region = fragmentsStep
				.locator("[data-testid='cut-region']")
				.first()
			await region.waitFor({ timeout: 5_000 })
			await region.click({ button: "right", force: true })

			await fragmentsStep.getByText("Удалить").click()

			await expect(
				fragmentsStep.getByText(
					`Фрагментов: ${MOCK_SEGMENTS.length - 1}`,
				),
			).toBeVisible()
		})

		test("should update info bar correctly after multiple operations", async ({
			fragmentsPage,
		}) => {
			const { fragmentsStep } = fragmentsPage

			// Delete first region
			let region = fragmentsStep
				.locator("[data-testid='cut-region']")
				.first()
			await region.waitFor({ timeout: 5_000 })
			await region.click({ button: "right", force: true })
			await fragmentsStep.getByText("Удалить").click()

			await expect(
				fragmentsStep.getByText(
					`Фрагментов: ${MOCK_SEGMENTS.length - 1}`,
				),
			).toBeVisible()

			// Delete second region
			region = fragmentsStep
				.locator("[data-testid='cut-region']")
				.first()
			await region.click({ button: "right", force: true })
			await fragmentsStep.getByText("Удалить").click()

			await expect(
				fragmentsStep.getByText(
					`Фрагментов: ${MOCK_SEGMENTS.length - 2}`,
				),
			).toBeVisible()

			// Add a new one
			const timeline = fragmentsStep
				.locator("[class*='timelineContainer']")
				.first()
			await timeline.click({ button: "right", position: { x: 100, y: 50 } })
			await fragmentsStep.getByText("Добавить новый").click()

			// 4 - 2 + 1 = 3
			await expect(
				fragmentsStep.getByText(
					`Фрагментов: ${MOCK_SEGMENTS.length - 2 + 1}`,
				),
			).toBeVisible()
		})
	})

	test.describe("Skip When No Regions", () => {
		test("should show skip button when all regions are removed", async ({
			fragmentsPage,
		}) => {
			const { fragmentsStep } = fragmentsPage

			// Remove all regions one by one
			for (let i = 0; i < MOCK_SEGMENTS.length; i++) {
				const region = fragmentsStep
					.locator("[data-testid='cut-region']")
					.first()
				await region.waitFor({ timeout: 5_000 })
				await region.click({ button: "right", force: true })
				await fragmentsStep.getByText("Удалить").click()
			}

			await expect(
				fragmentsStep.getByText("Фрагментов: 0"),
			).toBeVisible()
			await expect(
				fragmentsStep.getByText("Будет удалено: 0с"),
			).toBeVisible()
			await expect(
				fragmentsStep.getByRole("button", { name: "Пропустить" }),
			).toBeVisible()
		})

		test("should advance to transcription settings when skip is clicked", async ({
			fragmentsPage,
		}) => {
			const { page, fragmentsStep } = fragmentsPage

			// Remove all regions
			for (let i = 0; i < MOCK_SEGMENTS.length; i++) {
				const region = fragmentsStep
					.locator("[data-testid='cut-region']")
					.first()
				await region.waitFor({ timeout: 5_000 })
				await region.click({ button: "right", force: true })
				await fragmentsStep.getByText("Удалить").click()
			}

			await fragmentsStep
				.getByRole("button", { name: "Пропустить" })
				.click()

			await expect(
				page.locator("[data-testid='TranscriptionSettingsStep']"),
			).toBeVisible({ timeout: 10_000 })
		})
	})

	test.describe("Apply with Regions", () => {
		test("should send correct request body when apply is clicked", async ({
			fragmentsPage,
		}) => {
			const { page, fragmentsStep, projectId } = fragmentsPage

			let postBody: Record<string, unknown> | null = null
			page.on("request", (req) => {
				if (
					req.url().includes("/api/tasks/silence-apply/") &&
					req.method() === "POST"
				) {
					postBody = req.postDataJSON()
				}
			})

			await fragmentsStep
				.getByRole("button", { name: "Применить" })
				.click()

			// Wait for request to be sent
			await expect
				.poll(() => postBody, { timeout: 5_000 })
				.not.toBeNull()

			expect(postBody!.project_id).toBe(projectId)
			expect(postBody!.file_key).toBeTruthy()
			expect((postBody!.output_name as string)).toContain("Без тишины")
			expect((postBody!.cuts as unknown[]).length).toBe(
				MOCK_SEGMENTS.length,
			)
		})

		test("should disable buttons while apply is pending", async ({
			fragmentsPage,
		}) => {
			const { page, fragmentsStep } = fragmentsPage

			// Delay the apply response
			await page.route(
				"**/api/tasks/silence-apply/**",
				async (route) => {
					await new Promise((r) => setTimeout(r, 3000))
					await route.fulfill({
						status: 200,
						contentType: "application/json",
						body: JSON.stringify({ job_id: "fake-apply-job" }),
					})
				},
			)
			await page.route(
				"**/api/tasks/silence-apply/",
				async (route) => {
					await new Promise((r) => setTimeout(r, 3000))
					await route.fulfill({
						status: 200,
						contentType: "application/json",
						body: JSON.stringify({ job_id: "fake-apply-job" }),
					})
				},
			)

			await fragmentsStep
				.getByRole("button", { name: "Применить" })
				.click()

			await expect(
				fragmentsStep.getByRole("button", { name: "Применить" }),
			).toBeDisabled({ timeout: 2_000 })
			await expect(
				fragmentsStep.getByRole("button", { name: "Отмена" }),
			).toBeDisabled()
		})

		test("should show processing step after successful apply submission", async ({
			fragmentsPage,
		}) => {
			const { page, fragmentsStep } = fragmentsPage

			// Mock successful apply response
			await page.route("**/api/tasks/silence-apply/**", async (route) => {
				await route.fulfill({
					status: 200,
					contentType: "application/json",
					body: JSON.stringify({ job_id: "apply-job-123" }),
				})
			})
			await page.route("**/api/tasks/silence-apply/", async (route) => {
				await route.fulfill({
					status: 200,
					contentType: "application/json",
					body: JSON.stringify({ job_id: "apply-job-123" }),
				})
			})

			await fragmentsStep
				.getByRole("button", { name: "Применить" })
				.click()

			await expect(
				page.locator("[data-testid='ProcessingStep']"),
			).toBeVisible({ timeout: 10_000 })
		})

		test("should advance to transcription settings and submit processed file after apply completes", async ({
			fragmentsPage,
		}) => {
			const { page, fragmentsStep } = fragmentsPage

			const applyJobId = "apply-job-123"
			const processedFilePath =
				"users/test-user/output_files/silent/Без тишины test-video.mp4"
			const processedFileUrl = "https://example.com/processed-video.mp4"
			let transcriptionBody: Record<string, unknown> | null = null

			await page.route("**/api/tasks/silence-apply/**", async (route) => {
				await route.fulfill({
					status: 200,
					contentType: "application/json",
					body: JSON.stringify({ job_id: applyJobId }),
				})
			})
			await page.route("**/api/tasks/silence-apply/", async (route) => {
				await route.fulfill({
					status: 200,
					contentType: "application/json",
					body: JSON.stringify({ job_id: applyJobId }),
				})
			})
			await page.unroute("**/api/tasks/status/**")
			await page.route("**/api/tasks/status/**", async (route) => {
				const url = route.request().url()
				if (url.includes(applyJobId)) {
					await route.fulfill({
						status: 200,
						contentType: "application/json",
						body: JSON.stringify({
							status: "DONE",
							job_type: "SILENCE_APPLY",
							progress_pct: 100,
							output_data: {
								file_id: "00000000-0000-0000-0000-000000000071",
								file_path: processedFilePath,
							},
						}),
					})
					return
				}

				await route.fulfill({
					status: 200,
					contentType: "application/json",
					body: JSON.stringify({
						status: "DONE",
						job_type: "SILENCE_DETECT",
						progress_pct: 100,
						output_data: {
							silent_segments: MOCK_SEGMENTS,
							duration_ms: MOCK_DURATION_MS,
						},
					}),
				})
			})

			page.on("request", (req) => {
				if (
					req.url().includes("/api/tasks/transcription-generate/") &&
					req.method() === "POST"
				) {
					transcriptionBody = req.postDataJSON()
				}
			})
			await page.route(
				"**/api/tasks/transcription-generate/**",
				async (route) => {
					await route.fulfill({
						status: 200,
						contentType: "application/json",
						body: JSON.stringify({ job_id: "transcription-job-123" }),
					})
				},
			)
			await page.route(
				"**/api/tasks/transcription-generate/",
				async (route) => {
					await route.fulfill({
						status: 200,
						contentType: "application/json",
						body: JSON.stringify({ job_id: "transcription-job-123" }),
					})
				},
			)

			await fragmentsStep
				.getByRole("button", { name: "Применить" })
				.click()

			await expect(
				page.locator("[data-testid='ProcessingStep']"),
			).toBeVisible({ timeout: 10_000 })
			await expect(
				page.locator("[data-testid='TranscriptionSettingsStep']"),
			).toBeVisible({ timeout: 10_000 })

			await page
				.locator("[data-testid='TranscriptionSettingsStep']")
				.getByRole("button", { name: "Сгенерировать субтитры" })
				.click()

			await expect
				.poll(() => transcriptionBody, { timeout: 5_000 })
				.not.toBeNull()
			expect(
				(transcriptionBody as { file_key?: string } | null)?.file_key,
			).toBe(processedFilePath)
		})

		test("should stay on fragments step when apply fails", async ({
			fragmentsPage,
		}) => {
			const { page, fragmentsStep } = fragmentsPage

			// Mock failed apply response
			await page.route("**/api/tasks/silence-apply/**", async (route) => {
				await route.fulfill({
					status: 500,
					contentType: "application/json",
					body: JSON.stringify({ detail: "Internal Server Error" }),
				})
			})
			await page.route("**/api/tasks/silence-apply/", async (route) => {
				await route.fulfill({
					status: 500,
					contentType: "application/json",
					body: JSON.stringify({ detail: "Internal Server Error" }),
				})
			})

			await fragmentsStep
				.getByRole("button", { name: "Применить" })
				.click()

			// Should stay on fragments step
			await page.waitForTimeout(2000)
			await expect(fragmentsStep).toBeVisible()

			// Buttons should be re-enabled
			await expect(
				fragmentsStep.getByRole("button", { name: "Применить" }),
			).toBeEnabled({ timeout: 5_000 })
			await expect(
				fragmentsStep.getByRole("button", { name: "Отмена" }),
			).toBeEnabled()
		})
	})

	test.describe("Navigation", () => {
		test("should navigate back when cancel button is clicked", async ({
			fragmentsPage,
		}) => {
			const { page, fragmentsStep } = fragmentsPage

			await fragmentsStep
				.getByRole("button", { name: "Отмена" })
				.click()

			// Should go back to the previous step
			await expect(fragmentsStep).not.toBeVisible({ timeout: 5_000 })

			// The previous step in the wizard should be visible
			const processingOrSettings = page
				.locator(
					"[data-testid='ProcessingStep'], [data-testid='SilenceSettingsStep']",
				)
				.first()
			await expect(processingOrSettings).toBeVisible({ timeout: 10_000 })
		})
	})

	test.describe("State Persistence", () => {
		test("should restore fragments step after page reload", async ({
			fragmentsPage,
		}) => {
			const { page, fragmentsStep } = fragmentsPage

			// Wait for debounced save
			await page.waitForTimeout(2500)

			// Reload page — re-add route interceptor for task status
			await page.reload()
			await page.route("**/api/tasks/status/**", async (route) => {
				await route.fulfill({
					status: 200,
					contentType: "application/json",
					body: JSON.stringify({
						status: "DONE",
						progress_pct: 100,
						output_data: {
							silent_segments: MOCK_SEGMENTS,
							duration_ms: MOCK_DURATION_MS,
						},
					}),
				})
			})

			await page
				.locator("[data-testid='ProjectWizard']")
				.waitFor({ timeout: 10_000 })

			// Should restore to fragments step
			await expect(
				page.locator("[data-testid='FragmentsStep']"),
			).toBeVisible({ timeout: 15_000 })

			// Regions should re-load from mock data
			await expect(
				page.getByText(`Фрагментов: ${MOCK_SEGMENTS.length}`),
			).toBeVisible({ timeout: 10_000 })
		})
	})
})
