import { test, expect } from "@playwright/test";
import { pw_kanban_login } from "../../../../frontend/functions/browser/pw_kanban_login";

const USER = process.env.KANBAN_USER || "e2e_user";
const PWD = process.env.KANBAN_PWD || "e2e_test_pw_2026";

/**
 * Issue followup: drag lag.
 * Capture per-frame durations via requestAnimationFrame while a card is dragged
 * across reorder positions inside a populated column. Asserts p50 < 32ms and
 * max < 120ms so a regression visibly slower than ~30 fps fails the suite.
 *
 * Read each measurement printed to console to track changes over time.
 */
test.describe("kanban drag perf", () => {
  test("reorder inside HACIENDO does not drop below 30 fps", async ({ page }) => {
    await page.goto("/");
    await pw_kanban_login(page, { username: USER, password: PWD });

    // Wait until at least one card is mounted.
    await page.waitForSelector("[data-card-id]", { timeout: 10_000 });

    // Inject a tiny frame-time recorder.
    await page.evaluate(() => {
      const w = window as unknown as {
        _frames: number[];
        _capturing: boolean;
        _startCapture: () => void;
        _stopCapture: () => number[];
      };
      w._frames = [];
      w._capturing = false;
      let prev = 0;
      const tick = (t: number) => {
        if (!w._capturing) return;
        if (prev !== 0) w._frames.push(t - prev);
        prev = t;
        requestAnimationFrame(tick);
      };
      w._startCapture = () => {
        w._frames = [];
        w._capturing = true;
        prev = 0;
        requestAnimationFrame(tick);
      };
      w._stopCapture = () => {
        w._capturing = false;
        return w._frames.slice();
      };
    });

    // Pick the column with the MOST cards (worst-case reorder cost).
    const target = await page.evaluate(() => {
      const cols = Array.from(document.querySelectorAll<HTMLElement>("[data-column-id]"));
      let best: { columnId: string | null; cardIds: string[] } | null = null;
      for (const col of cols) {
        const cards = Array.from(col.querySelectorAll<HTMLElement>("[data-card-id]"));
        if (!best || cards.length > best.cardIds.length) {
          best = {
            columnId: col.getAttribute("data-column-id"),
            cardIds: cards
              .map((c) => c.getAttribute("data-card-id"))
              .filter((x): x is string => x !== null),
          };
        }
      }
      return best && best.cardIds.length >= 3 ? best : null;
    });
    if (!target) test.skip(true, "need a column with >= 3 cards");

    const firstId = target!.cardIds[0]!;
    const lastId = target!.cardIds[target!.cardIds.length - 1]!;

    const source = page.locator(`[data-card-id="${firstId}"]`);
    const targetEl = page.locator(`[data-card-id="${lastId}"]`);
    const sb = await source.boundingBox();
    const tb = await targetEl.boundingBox();
    if (!sb || !tb) throw new Error("no bounding box");

    const sx = sb.x + sb.width / 2;
    const sy = sb.y + sb.height / 2;
    const tx = tb.x + tb.width / 2;
    const ty = tb.y + tb.height / 2;

    await page.mouse.move(sx, sy);
    await page.mouse.down();
    // dnd-kit pointer-sensor activation threshold: 8px; nudge horizontally first.
    await page.mouse.move(sx + 12, sy, { steps: 2 });

    // Probe how many KanbanCard renders happen during the drag.
    await page.evaluate(() => {
      const w = window as unknown as { _cardRenderCount: number; _cardRenderProbe: boolean };
      w._cardRenderCount = 0;
      w._cardRenderProbe = true;
    });

    await page.evaluate(() => (window as unknown as { _startCapture: () => void })._startCapture());

    // Move slowly across the column to trigger reorder swaps; steps=40 gives
    // dnd-kit time to recompute positions.
    await page.mouse.move(tx, ty, { steps: 40 });
    // Hover so any final layout animation captures into the trace.
    await page.waitForTimeout(120);

    const frames = (await page.evaluate(() =>
      (window as unknown as { _stopCapture: () => number[] })._stopCapture()
    )) as number[];
    const renderCount = (await page.evaluate(
      () => (window as unknown as { _cardRenderCount: number })._cardRenderCount
    )) as number;
    const bodyCount = (await page.evaluate(
      () => (window as unknown as { _cardBodyRenderCount: number })._cardBodyRenderCount || 0
    )) as number;
    console.log(`drag-perf wrapper-renders=${renderCount} body-renders=${bodyCount} over ${frames.length} frames`);

    await page.mouse.up();

    const sorted = [...frames].sort((a, b) => a - b);
    const p50 = sorted[Math.floor(sorted.length * 0.5)] ?? 0;
    const p95 = sorted[Math.floor(sorted.length * 0.95)] ?? 0;
    const max = sorted.length > 0 ? sorted[sorted.length - 1] : 0;
    const avg = sorted.reduce((a, b) => a + b, 0) / Math.max(1, sorted.length);

    console.log(
      `drag-perf frames=${sorted.length} avg=${avg.toFixed(1)}ms p50=${p50.toFixed(1)}ms p95=${p95.toFixed(1)}ms max=${max.toFixed(1)}ms`
    );

    // Save a stable artefact so we can compare runs.
    test.info().annotations.push({
      type: "drag-perf",
      description: JSON.stringify({ count: sorted.length, avg, p50, p95, max }),
    });

    // Thresholds tuned tras separar el body memoizado (issue dnd-lag-fix
    // followup). Pre-fix: p95=83ms / max=117ms. Post-fix: p95=33 / max=33.
    expect(p50).toBeLessThan(20);
    expect(p95).toBeLessThan(50);
    expect(max).toBeLessThan(60);
    // Body memoizado: durante el drag no debe re-renderizar.
    expect(bodyCount).toBeLessThan(5);
  });
});
