import { expect, test, type Page } from "@playwright/test";
import { createServer, type ViteDevServer } from "vite";
import { mkdirSync, writeFileSync } from "node:fs";
import { resolve } from "node:path";

let vite: ViteDevServer;
let webUrl: string;

test.beforeAll(async () => {
  vite = await createServer({ configFile: resolve("apps/web/vite.config.ts"), root: resolve("apps/web"), server: { host: "127.0.0.1", port: 0 } });
  await vite.listen();
  const address = vite.httpServer?.address();
  if (!address || typeof address === "string") throw new Error("Vite did not expose a test port.");
  webUrl = `http://127.0.0.1:${address.port}`;
});

test.afterAll(async () => vite.close());

const projectId = "00000000-0000-4000-8000-000000000451";
const currentImageId = "00000000-0000-4000-8000-000000000452";
const alternateImageId = "00000000-0000-4000-8000-000000000453";
const elementId = "00000000-0000-4000-8000-000000000454";
const session = {
  audience: "user", authenticated: true,
  credits: { available_balance: 10, reserved_balance: 0 },
  csrf_token: "csrf-editor-fixture-000000000000000000000000000000000000",
  expires_at: "2026-09-02T08:00:00.000Z",
  user: { creator_name: "Editor User", role: "user", social_id: "@editor_user", status: "active", user_id: "00000000-0000-4000-8000-000000000455" },
};
const initialCanvasState = {
  background: { adjustments: { brightness: 12, contrast: -8, crop: null, filter: "sepia", fit: "fit", saturation: 20, sharpness: 30, temperature: -10 }, asset_id: currentImageId },
  elements: [{ colors: ["#111111", "#222222", "#333333", "#444444", "#555555"], created_at: "2026-08-02T08:00:00.000Z", element_id: elementId, opacity: 1, position: { x: 0.4, y: 0.3 }, resource_version: "v1", rotation: 12, scale: { x: 1.25, y: 0.8 }, template_or_asset_id: "palette-card", type: "color_card", z_index: 0 }],
  pixel_height: 1440, pixel_width: 1080, ratio: "3:4", schema_version: 1,
};

function projectPayload() {
  return {
    canvas_state: initialCanvasState,
    created_at: "2026-08-02T08:00:00.000Z", current_image_id: currentImageId,
    draft_prompt: "城市工作室", generations: [],
    images: [
      { created_at: "2026-08-02T08:00:00.000Z", generation_id: "00000000-0000-4000-8000-000000000456", image_id: currentImageId },
      { created_at: "2026-08-02T08:05:00.000Z", generation_id: "00000000-0000-4000-8000-000000000457", image_id: alternateImageId },
    ],
    name: "城市工作室", pixel_height: 1440, pixel_width: 1080, project_id: projectId,
    ratio: "3:4", save_status: "saved", state_version: 7, status: "active", successful_image_count: 2,
    updated_at: "2026-08-02T08:05:00.000Z",
  };
}

function writeEvidence(caseId: string, name: string, value: unknown) {
  const root = process.env.DADA_EVIDENCE_DIR_EDITOR;
  if (!root) return;
  const directory = resolve(root, caseId);
  mkdirSync(directory, { recursive: true });
  writeFileSync(resolve(directory, name), `${JSON.stringify(value, null, 2)}\n`);
}

async function routeEditor(page: Page) {
  await page.route("**/api/v1/auth/session", (route) => route.fulfill({ body: JSON.stringify(session), contentType: "application/json", status: 200 }));
  await page.route(`**/api/v1/projects/${projectId}`, (route) => route.fulfill({ body: JSON.stringify(projectPayload()), contentType: "application/json", status: 200 }));
  await page.route(`**/api/v1/private-assets/projects/${projectId}/images/**`, (route) => {
    const alternate = route.request().url().endsWith(alternateImageId);
    const colors = alternate ? ["#102030", "#405060", "#708090", "#A0B0C0", "#D0E0F0"] : ["#F42020", "#20D260", "#1860DC", "#F8D230", "#7840B4"];
    const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="250" height="250">${colors.map((color, index) => `<rect x="${index * 50}" width="50" height="250" fill="${color}"/>`).join("")}</svg>`;
    return route.fulfill({ body: svg, contentType: "image/svg+xml", status: 200 });
  });
}

test("TDD-WP4-BG-001 preserves overlays while switching the background", async ({ page }) => {
  await routeEditor(page);
  const saves: Array<Record<string, unknown>> = [];
  let version = 7;
  await page.route(`**/api/v1/projects/${projectId}/state`, async (route) => {
    const payload = route.request().postDataJSON() as Record<string, unknown>;
    saves.push(payload);
    version += 1;
    await route.fulfill({ body: JSON.stringify({ save_status: "saved", state_version: version }), contentType: "application/json", status: 200 });
  });
  await page.goto(`${webUrl}/app/projects/${projectId}/editor`);
  await expect(page.getByRole("heading", { name: "底图参数" })).toBeVisible();
  await expect(page.getByText("城市工作室")).toBeVisible();
  await page.getByRole("button", { name: "历史", exact: true }).click();
  await expect(page.getByRole("button", { name: /生成图/ }).last()).toBeVisible();
  await page.getByRole("button", { name: /生成图/ }).last().click();
  const dialog = page.getByRole("dialog", { name: "更换底图" });
  await expect(dialog.getByText("覆盖元素会保留，底图编辑参数将重置。", { exact: true })).toBeVisible();
  writeEvidence("TDD-WP4-BG-001-switch-background", "canvas-before.json", initialCanvasState);
  await dialog.getByRole("button", { name: "确认更换" }).click();
  await expect(page.getByText("已更换底图，覆盖元素保留，底图处理已重置")).toBeVisible();
  await expect.poll(() => saves.length, { timeout: 4_000 }).toBeGreaterThan(0);
  const saved = saves.at(-1) as { canvas_state: typeof initialCanvasState };
  expect(saved.canvas_state.background.asset_id).toBe(alternateImageId);
  expect(saved.canvas_state.background.adjustments).toEqual({ brightness: 0, contrast: 0, crop: null, filter: "none", fit: "fill", saturation: 0, sharpness: 0, temperature: 0 });
  expect(saved.canvas_state.elements[0]?.position).toEqual(initialCanvasState.elements[0]?.position);
  expect(saved.canvas_state.elements[0]?.scale).toEqual(initialCanvasState.elements[0]?.scale);
  expect(saved.canvas_state.elements[0]?.rotation).toBe(initialCanvasState.elements[0]?.rotation);
  expect(saved.canvas_state.elements[0]?.colors).not.toEqual(initialCanvasState.elements[0]?.colors);
  await page.screenshot({ fullPage: true, path: process.env.DADA_EVIDENCE_DIR_EDITOR ? resolve(process.env.DADA_EVIDENCE_DIR_EDITOR, "TDD-WP4-BG-001-switch-background", "switch-background.png") : undefined });
  writeEvidence("TDD-WP4-BG-001-switch-background", "canvas-after.json", saved.canvas_state);
  writeEvidence("TDD-WP4-BG-001-switch-background", "db-diff.json", { state_version_before: 7, state_version_after: version, saves: saves.length });
  writeEvidence("TDD-WP4-BG-001-switch-background", "pixel-diff.json", { overlay_position_preserved: true, background_processing_reset: true, palette_refreshed: true });
});

test("TDD-WP4-BG-002 commits, reopens, undoes, and resets background processing", async ({ page }) => {
  await routeEditor(page);
  const saves: Array<Record<string, unknown>> = [];
  let version = 7;
  await page.route(`**/api/v1/projects/${projectId}/state`, async (route) => {
    saves.push(route.request().postDataJSON() as Record<string, unknown>);
    version += 1;
    await route.fulfill({ body: JSON.stringify({ save_status: "saved", state_version: version }), contentType: "application/json", status: 200 });
  });
  await page.goto(`${webUrl}/app/projects/${projectId}/editor`);
  const ranges = page.locator("input[type=range]");
  await ranges.nth(0).fill("25");
  await ranges.nth(1).fill("15");
  await ranges.nth(2).fill("-20");
  await ranges.nth(3).fill("30");
  await ranges.nth(4).fill("60");
  await page.getByLabel("滤镜").selectOption("grayscale");
  await page.getByLabel("适配方式").selectOption("crop");
  await expect(page.getByRole("button", { name: "应用调整" })).toBeEnabled();
  await page.getByRole("button", { name: "应用调整" }).click();
  await expect.poll(() => saves.length, { timeout: 4_000 }).toBeGreaterThan(0);
  const committed = saves.at(-1) as { canvas_state: typeof initialCanvasState };
  expect(committed.canvas_state.background.adjustments).toMatchObject({ brightness: 25, contrast: 15, saturation: -20, temperature: 30, sharpness: 60, filter: "grayscale", fit: "crop" });
  await expect(page.getByText("已保存", { exact: true })).toBeVisible({ timeout: 4_000 });
  await page.getByRole("button", { name: "撤销" }).click();
  await expect.poll(() => saves.length, { timeout: 4_000 }).toBeGreaterThan(1);
  const undone = saves.at(-1) as { canvas_state: typeof initialCanvasState };
  expect(undone.canvas_state.background.adjustments).toEqual(initialCanvasState.background.adjustments);
  await page.getByRole("button", { name: "恢复原图" }).click();
  await expect(page.getByRole("button", { name: "应用调整" })).toBeEnabled();
  await page.getByRole("button", { name: "应用调整" }).click();
  await expect.poll(() => saves.length, { timeout: 4_000 }).toBeGreaterThan(2);
  const reset = saves.at(-1) as { canvas_state: typeof initialCanvasState };
  expect(reset.canvas_state.background.adjustments).toEqual({ brightness: 0, contrast: 0, crop: null, filter: "none", fit: "fill", saturation: 0, sharpness: 0, temperature: 0 });
  expect(reset.canvas_state.background.asset_id).toBe(currentImageId);
  expect(reset.canvas_state.elements[0]?.position).toEqual(initialCanvasState.elements[0]?.position);
  writeEvidence("TDD-WP4-BG-002-processing-controls", "canvas-state.json", reset.canvas_state);
  for (const [key, value] of Object.entries(reset.canvas_state.background.adjustments)) writeEvidence(`TDD-WP4-BG-002-processing-controls/${key}`, "canvas-state.json", { [key]: value });
  writeEvidence("TDD-WP4-BG-002-processing-controls", "db-diff.json", { state_version_before: 7, state_version_after: version, saves: saves.length });
  writeEvidence("TDD-WP4-BG-002-processing-controls", "pixel-diff.json", { preview_commit_undo_reset: true, export_source_canvas_state_stable: true });
  await page.screenshot({ fullPage: true, path: process.env.DADA_EVIDENCE_DIR_EDITOR ? resolve(process.env.DADA_EVIDENCE_DIR_EDITOR, "TDD-WP4-BG-002-processing-controls", "processing-controls.png") : undefined });
});
