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

test("scheduled conditions keep acrylic blur outside transformed ancestors", async ({ page }) => {
  await page.goto("/", { waitUntil: "domcontentloaded" });

  const panel = page.getByRole("region", { name: "工况任务", exact: true });
  await expect(panel).toBeVisible();

  await expectAcrylicComposition(panel);
  await expect(panel).toHaveCSS("width", "432px");
  const timeline = panel.getByTestId("scheduled-condition-timeline");
  const detailViewport = panel.locator(".scheduled-feed-detail-viewport");
  const detailScroll = panel.getByTestId("scheduled-condition-detail-scroll");
  const collapsedPanelBox = await panel.boundingBox();
  const collapsedTimelineBox = await timeline.boundingBox();
  expect(collapsedPanelBox).not.toBeNull();
  expect(collapsedTimelineBox).not.toBeNull();

  await panel.getByRole("button", { name: "展开工况任务" }).click();

  await expectAcrylicComposition(panel);
  await expect(panel).toHaveCSS("width", "880px");
  const expandedPanelBox = await panel.boundingBox();
  const expandedTimelineBox = await timeline.boundingBox();
  const expandedDetailViewportBox = await detailViewport.boundingBox();
  const expandedDetailScrollBox = await detailScroll.boundingBox();
  expect(expandedPanelBox).not.toBeNull();
  expect(expandedTimelineBox).not.toBeNull();
  expect(expandedDetailViewportBox).not.toBeNull();
  expect(expandedDetailScrollBox).not.toBeNull();
  expect(Math.round(expandedPanelBox!.x + expandedPanelBox!.width)).toBe(
    Math.round(collapsedPanelBox!.x + collapsedPanelBox!.width)
  );
  expect(Math.round(expandedTimelineBox!.width)).toBe(
    Math.round(collapsedTimelineBox!.width)
  );
  expect(Math.round(expandedTimelineBox!.x + expandedTimelineBox!.width)).toBe(
    Math.round(collapsedTimelineBox!.x + collapsedTimelineBox!.width)
  );
  expect(expandedTimelineBox!.y + expandedTimelineBox!.height).toBeLessThanOrEqual(
    expandedPanelBox!.y + expandedPanelBox!.height - 12
  );
  expect(Math.round(expandedDetailViewportBox!.y + expandedDetailViewportBox!.height)).toBe(
    Math.round(expandedTimelineBox!.y + expandedTimelineBox!.height)
  );
  expect(expandedDetailScrollBox!.y).toBeGreaterThanOrEqual(
    expandedDetailViewportBox!.y + 8
  );
  expect(expandedDetailScrollBox!.y + expandedDetailScrollBox!.height).toBeLessThanOrEqual(
    expandedDetailViewportBox!.y + expandedDetailViewportBox!.height - 8
  );
  await expect(detailViewport).toHaveCSS("border-radius", "12px");
  const timelineScroll = timeline.locator(".scheduled-feed-scroll.flex-1");
  expect(await readScrollbarPresentation(detailScroll)).toEqual(
    await readScrollbarPresentation(timelineScroll)
  );
  const futureScrollBox = await timeline.locator(".scheduled-feed-scroll").first().boundingBox();
  const recentScrollBox = await timelineScroll.boundingBox();
  expect(futureScrollBox).not.toBeNull();
  expect(recentScrollBox).not.toBeNull();
  expect(Math.round(futureScrollBox!.x + futureScrollBox!.width)).toBe(
    Math.round(recentScrollBox!.x + recentScrollBox!.width)
  );

  await panel.getByRole("button", { name: "收起工况任务" }).click();
  await expect(panel).toHaveCSS("width", "432px");
  const restoredTimelineBox = await timeline.boundingBox();
  expect(restoredTimelineBox).not.toBeNull();
  expect(Math.round(restoredTimelineBox!.width)).toBe(
    Math.round(collapsedTimelineBox!.width)
  );
});

test("narrow desktop keeps Agent and expanded conditions from overlapping", async ({ page }) => {
  await page.setViewportSize({ width: 1024, height: 768 });
  await page.goto("/", { waitUntil: "domcontentloaded" });
  await page.getByRole("button", { name: "工况任务", exact: true }).click();

  const panel = page.getByRole("region", { name: "工况任务", exact: true });
  const agentPanel = page.locator('aside[aria-label="Agent 命令面板"]');
  const resizeHandle = page.getByRole("separator", { name: "调整 Agent 面板宽度" });
  await resizeHandle.focus();
  await page.keyboard.press("End");
  await expect(agentPanel).toHaveCSS("width", "484px");

  const compactAgentBox = await agentPanel.boundingBox();
  const compactConditionBox = await panel.boundingBox();
  expect(compactAgentBox).not.toBeNull();
  expect(compactConditionBox).not.toBeNull();
  expect(compactConditionBox!.x - (compactAgentBox!.x + compactAgentBox!.width)).toBeGreaterThanOrEqual(
    24
  );

  await panel.getByRole("button", { name: "展开工况任务" }).click();
  await expect(page.locator('aside[aria-label="Agent 折叠栏"]')).toBeVisible();
  await expect(panel).toHaveCSS("width", "852px");

  const expandedConditionBox = await panel.boundingBox();
  const collapsedAgentBox = await page
    .locator('aside[aria-label="Agent 折叠栏"]')
    .boundingBox();
  expect(expandedConditionBox).not.toBeNull();
  expect(collapsedAgentBox).not.toBeNull();
  expect(
    expandedConditionBox!.x - (collapsedAgentBox!.x + collapsedAgentBox!.width)
  ).toBeGreaterThanOrEqual(24);

  await panel.getByRole("button", { name: "收起工况任务" }).click();
  await expect(agentPanel).toBeVisible();
});

test("switching conditions keeps the detail viewport fully expanded", async ({ page }) => {
  await page.goto("/", { waitUntil: "domcontentloaded" });

  const panel = page.getByRole("region", { name: "工况任务", exact: true });
  await panel.getByRole("button", { name: "展开工况任务" }).click();
  await expect(panel).toHaveCSS("width", "880px");

  const detailScroll = panel.getByTestId("scheduled-condition-detail-scroll");
  const baselineHeight = await detailScroll.evaluate(
    (element) => element.getBoundingClientRect().height
  );
  const targetCondition = panel
    .getByTestId("scheduled-condition-timeline")
    .getByRole("button", { name: /SCADA 数据诊断/ })
    .nth(1);
  const sampledHeights = await targetCondition.evaluate(async (element) => {
    element.click();
    const heights: number[] = [];
    const startedAt = performance.now();

    await new Promise<void>((resolve) => {
      const sample = () => {
        const detail = document.querySelector<HTMLElement>(
          '[data-testid="scheduled-condition-detail-scroll"]'
        );
        if (detail) {
          heights.push(detail.getBoundingClientRect().height);
        }

        if (performance.now() - startedAt >= 650) {
          resolve();
          return;
        }

        requestAnimationFrame(sample);
      };

      requestAnimationFrame(sample);
    });

    return heights;
  });

  expect(sampledHeights.length).toBeGreaterThan(1);
  expect(Math.min(...sampledHeights)).toBeGreaterThanOrEqual(baselineHeight - 1);
  await panel
    .getByTestId("scheduled-condition-timeline")
    .getByRole("button", { name: /SCADA 数据诊断/ })
    .first()
    .click();
  const workflowTitle = panel.getByText("SCADA 诊断流程", { exact: true });
  await expect(workflowTitle).toBeVisible();
  await expect(workflowTitle.locator("..").locator("svg")).toHaveCount(0);
});

test("keeps execution spinners without breathing badges after selecting timeline tasks", async ({
  page
}) => {
  await page.goto("/", { waitUntil: "domcontentloaded" });

  const panel = page.getByRole("region", { name: "工况任务", exact: true });
  await panel.getByRole("button", { name: "展开工况任务" }).click();
  const timeline = panel.getByTestId("scheduled-condition-timeline");
  const runningCondition = timeline
    .locator('button[aria-pressed]')
    .filter({ hasText: "SCADA 数据诊断" })
    .filter({ hasText: "执行中" })
    .first();
  const executingWorkOrder = timeline
    .locator('button[aria-pressed]')
    .filter({ hasText: "系统工单：北辰分区阀门开度调整" });

  await expectRunningStatusAnimations(runningCondition);
  await runningCondition.click();
  await expectRunningStatusAnimations(runningCondition);
  await expect(
    panel
      .getByText("SCADA 诊断流程", { exact: true })
      .locator("..")
      .locator(".status-badge .status-dot")
  ).toHaveCount(0);

  await expectRunningStatusAnimations(executingWorkOrder);
  await expect(executingWorkOrder.getByText("执行中", { exact: true })).toBeVisible();
  await executingWorkOrder.click();
  await expectRunningStatusAnimations(executingWorkOrder);
});

test("shows completed field work as a static waiting-for-reply state", async ({ page }) => {
  await page.goto("/", { waitUntil: "domcontentloaded" });

  const panel = page.getByRole("region", { name: "工况任务", exact: true });
  await panel.getByRole("button", { name: "展开工况任务" }).click();
  const awaitingReplyWorkOrder = panel
    .getByTestId("scheduled-condition-timeline")
    .locator('button[aria-pressed]')
    .filter({ hasText: "系统工单：泵站边界复核复令" });

  await expect(awaitingReplyWorkOrder.getByText("待复令", { exact: true })).toBeVisible();
  await expect(awaitingReplyWorkOrder.locator(".status-icon")).toHaveAttribute(
    "data-status-tone",
    "warning"
  );
  await expect(awaitingReplyWorkOrder.locator(".status-glyph")).toHaveAttribute(
    "data-activity",
    "static"
  );
  await expect.poll(() => getAnimationName(awaitingReplyWorkOrder.locator(".status-glyph"))).toBe(
    "none"
  );

  await awaitingReplyWorkOrder.click();
  await expect(panel.getByText("当前阶段：待复令", { exact: true })).toBeVisible();
  await expect(
    panel
      .getByTestId("scheduled-condition-detail-scroll")
      .locator('.status-badge[data-status-tone="warning"]')
      .filter({ hasText: "待复令" })
      .first()
  ).toHaveAttribute("data-status-tone", "warning");
});

async function readScrollbarPresentation(locator: Locator) {
  return locator.evaluate((element) => {
    const style = getComputedStyle(element);
    const scrollbarStyle = getComputedStyle(element, "::-webkit-scrollbar");
    const thumbStyle = getComputedStyle(element, "::-webkit-scrollbar-thumb");

    return {
      gutter: style.scrollbarGutter,
      overflowY: style.overflowY,
      scrollbarColor: style.scrollbarColor,
      scrollbarWidth: style.scrollbarWidth,
      thumbBackground: thumbStyle.backgroundColor,
      thumbRadius: thumbStyle.borderRadius,
      webkitWidth: scrollbarStyle.width
    };
  });
}

async function expectAcrylicComposition(panel: Locator) {
  const composition = await panel.evaluate((element) => {
    const rootStyle = getComputedStyle(element);
    const blockingAncestors: string[] = [];
    const filteredDescendants: string[] = [];

    let ancestor = element.parentElement;
    while (ancestor && ancestor.tagName !== "MAIN") {
      const style = getComputedStyle(ancestor);
      if (
        style.transform !== "none" ||
        style.filter !== "none" ||
        style.opacity !== "1" ||
        style.isolation !== "auto" ||
        style.willChange.includes("transform")
      ) {
        blockingAncestors.push(ancestor.className);
      }
      ancestor = ancestor.parentElement;
    }

    for (const descendant of element.querySelectorAll("*")) {
      if (getComputedStyle(descendant).backdropFilter !== "none") {
        filteredDescendants.push(descendant.className);
      }
    }

    return {
      backdropFilter: rootStyle.backdropFilter,
      blockingAncestors,
      filteredDescendants,
      transform: rootStyle.transform,
      willChange: rootStyle.willChange
    };
  });

  expect(composition.backdropFilter).toContain("blur(");
  expect(composition.blockingAncestors).toEqual([]);
  expect(composition.filteredDescendants).toEqual([]);
  expect(composition.transform).toBe("none");
  expect(composition.willChange).toBe("auto");
}

async function expectRunningStatusAnimations(row: Locator) {
  const timelineGlyph = row.locator(".status-icon .status-glyph");

  await expect(timelineGlyph).toHaveAttribute("data-activity", "loading");
  await expect(row.locator(".status-badge .status-dot")).toHaveCount(0);
  await expect.poll(() => getAnimationName(timelineGlyph)).toBe("status-spin");
}

async function getAnimationName(locator: Locator) {
  return locator.evaluate((element) => getComputedStyle(element).animationName);
}
