import { expect, test, type Page } from "@playwright/test";

test.beforeEach(async ({ page }) => {
  await prepareWorkbench(page);
});

test("desktop controls share focus, hover, press and open states", async ({ page }) => {
  await page.setViewportSize({ width: 1440, height: 900 });
  await page.goto("/", { waitUntil: "domcontentloaded" });

  const scenario = page.getByRole("button", { name: "切换模拟方案" });
  await expect(scenario).toBeVisible();
  await expect.poll(async () => (await scenario.boundingBox())?.height).toBe(32);

  const compactHitArea = await scenario.evaluate(
    (element) => getComputedStyle(element, "::after").inset
  );
  expect(compactHitArea).toBe("-4px");

  await scenario.focus();
  await expect
    .poll(async () => scenario.evaluate((element) => getComputedStyle(element).boxShadow))
    .not.toBe("none");

  await scenario.hover();
  await expect
    .poll(async () => scenario.evaluate((element) => getComputedStyle(element).backgroundColor))
    .not.toBe("rgba(0, 0, 0, 0)");

  const pressTarget = page.getByRole("button", { name: /任务浮条/ });
  const restingBackground = await pressTarget.evaluate(
    (element) => getComputedStyle(element).backgroundColor
  );
  expect(restingBackground).toBe("rgb(232, 241, 255)");
  await pressTarget.hover();
  const box = await pressTarget.boundingBox();
  expect(box).not.toBeNull();
  await page.mouse.move(box!.x + box!.width / 2, box!.y + box!.height / 2);
  await page.mouse.down();
  await expect
    .poll(async () => pressTarget.evaluate((element) => element.matches(":active")))
    .toBe(true);
  await expect
    .poll(async () => pressTarget.evaluate((element) => getComputedStyle(element).filter))
    .toBe("none");
  const pressedBox = await pressTarget.boundingBox();
  expect(pressedBox).toEqual(box);
  await expect
    .poll(async () => pressTarget.evaluate((element) => getComputedStyle(element).backgroundColor))
    .toBe("rgb(207, 226, 255)");
  await page.mouse.move(720, 460);
  await page.mouse.up();
  await expect
    .poll(async () => pressTarget.evaluate((element) => getComputedStyle(element).filter))
    .toBe("none");
  await expect(pressTarget).toHaveAttribute("aria-pressed", "true");
  await pressTarget.hover();
  const hoverBackground = await pressTarget.evaluate((element) => {
    const probe = document.createElement("span");
    probe.style.backgroundColor = "var(--action-selection-soft-hover)";
    element.append(probe);
    const backgroundColor = getComputedStyle(probe).backgroundColor;
    probe.remove();
    return backgroundColor;
  });
  await expect
    .poll(async () => pressTarget.evaluate((element) => getComputedStyle(element).backgroundColor))
    .toBe(hoverBackground);
  await page.mouse.move(720, 460);
  await expect
    .poll(async () => pressTarget.evaluate((element) => getComputedStyle(element).backgroundColor))
    .toBe(restingBackground);
  await page.getByRole("button", { name: "打开用户菜单" }).click();
  const userButton = page.getByRole("button", { name: "打开用户菜单" });
  await expect(userButton).toHaveAttribute("data-state", "open");
  await expect
    .poll(async () => userButton.evaluate((element) => getComputedStyle(element).backgroundColor))
    .not.toBe("rgba(0, 0, 0, 0)");

  const mapTool = page.getByRole("button", { name: /图层：管理地图图层/ });
  const mapToolBox = await mapTool.boundingBox();
  expect(mapToolBox?.width).toBe(40);
  expect(mapToolBox?.height).toBe(40);
});

test("mobile launchers and map dock keep non-overlapping touch targets", async ({ page }) => {
  await page.setViewportSize({ width: 375, height: 812 });
  await page.goto("/", { waitUntil: "domcontentloaded" });

  const agentLauncher = page.getByRole("button", { name: "打开 Agent 面板" });
  const conditionLauncher = page.getByRole("button", { name: "打开工况任务" });
  const mapTools = page.locator('nav[aria-label="地图工具"]:visible button:visible');

  for (const locator of [agentLauncher, conditionLauncher]) {
    const box = await locator.boundingBox();
    expect(box?.width).toBeGreaterThanOrEqual(44);
    expect(box?.height).toBeGreaterThanOrEqual(44);
  }

  const toolCount = await mapTools.count();
  expect(toolCount).toBeGreaterThan(0);
  for (let index = 0; index < toolCount; index += 1) {
    const box = await mapTools.nth(index).boundingBox();
    expect(box?.width).toBeGreaterThanOrEqual(40);
    expect(box?.height).toBeGreaterThanOrEqual(40);
  }

  const rectangles = await Promise.all(
    [agentLauncher, mapTools.first(), conditionLauncher].map((locator) => locator.boundingBox())
  );
  expect(rectangles.every(Boolean)).toBe(true);
  expect(rectangles[0]!.x + rectangles[0]!.width).toBeLessThanOrEqual(rectangles[1]!.x);
  expect(rectangles[1]!.x + rectangles[1]!.width).toBeLessThan(rectangles[2]!.x);
});

test("Agent content buttons do not gain a border while pressed", async ({ page }) => {
  await page.setViewportSize({ width: 1440, height: 900 });
  await page.goto("/", { waitUntil: "domcontentloaded" });

  await page.evaluate(() => {
    const conversation = document.createElement("div");
    conversation.className = "agent-panel-conversation";
    Object.assign(conversation.style, {
      position: "fixed",
      inset: "100px auto auto 100px",
      zIndex: "9999"
    });
    const button = document.createElement("button");
    button.type = "button";
    button.textContent = "测试内容按钮";
    Object.assign(button.style, { width: "120px", height: "40px" });
    conversation.append(button);
    document.body.append(conversation);
  });

  const contentButton = page.getByRole("button", { name: "测试内容按钮" });
  const box = await contentButton.boundingBox();
  expect(box).not.toBeNull();

  await page.mouse.move(box!.x + box!.width / 2, box!.y + box!.height / 2);
  await page.mouse.down();
  await expect
    .poll(async () => contentButton.evaluate((element) => element.matches(":active")))
    .toBe(true);
  await expect(contentButton).toHaveCSS("outline-style", "none");
  await page.mouse.up();
});

async function prepareWorkbench(page: Page) {
  await page.route("**/runtime-config.js", async (route) => {
    await route.fulfill({
      contentType: "application/javascript",
      body: `globalThis.__TJWATER_CONFIG__ = {
        TJWATER_MAPBOX_ACCESS_TOKEN: "",
        TJWATER_MAP_URL: "https://button-system.invalid/geoserver",
        TJWATER_GEOSERVER_WORKSPACE: "tjwater",
        TJWATER_AGENT_API_BASE_URL: "http://127.0.0.1:8787",
        TJWATER_ENABLE_DEV_PANEL: "false",
        TJWATER_ENABLE_MSW: "false"
      };`
    });
  });
  await page.route("https://button-system.invalid/**", async (route) => {
    await route.fulfill({ status: 204, body: "" });
  });
  await page.route("https://demotiles.maplibre.org/**", async (route) => {
    await route.fulfill({ status: 204, body: "" });
  });
  await page.route("**/*.riv", async (route) => {
    await route.abort();
  });
}
