import assert from "node:assert/strict";
import { test } from "bun:test";

import {
  MDTODO_TASK_TREE_PANE_CONTENT_SELECTOR,
  nodeWebObserveRunnerSamplingSource,
} from "./hwlab-node-web-observe-runner-sampling-source";
import { nodeWebObserveAnalyzerProjectSource } from "./hwlab-node-web-observe-analyzer-project-source";
import { nodeWebObserveRunnerControlSource } from "./hwlab-node-web-observe-runner-control-source";

test("MDTODO task tree pane gap treats the pinned facts footer as content", async () => {
  assert.match(
    MDTODO_TASK_TREE_PANE_CONTENT_SELECTOR,
    /\[data-testid="mdtodo-task-tree-facts"\]/u,
  );
  assert.match(
    MDTODO_TASK_TREE_PANE_CONTENT_SELECTOR,
    /\[data-testid="mdtodo-task-page-fact"\]/u,
  );

  const source = nodeWebObserveRunnerSamplingSource();
  assert.ok(
    source.includes(
      `measurePaneGap("task-tree", '[data-testid="mdtodo-task-tree"]', ${JSON.stringify(MDTODO_TASK_TREE_PANE_CONTENT_SELECTOR)})`,
    ),
  );
  assert.match(
    source,
    /semanticTaskCandidates\.length > 0 \? semanticTaskCandidates : fallbackTaskCandidates/u,
  );
  assert.match(
    source,
    /taskCandidates\.filter\(\(candidate\) => !taskRefElement\(candidate\)\)\.length/u,
  );
  assert.doesNotMatch(source, /taskCandidates\.length - taskItems\.length/u);
  assert.match(source, /mdtodo-body-read/u);
  assert.match(source, /mdtodo-launch-workbench/u);

  const child = Bun.spawn(["node", "--input-type=module", "--check", "-"], {
    stdin: "pipe",
    stdout: "pipe",
    stderr: "pipe",
  });
  child.stdin.write(source);
  child.stdin.end();
  const [stderr, exitCode] = await Promise.all([
    new Response(child.stderr).text(),
    child.exited,
  ]);
  assert.equal(exitCode, 0, stderr);
});

test("MDTODO pane gap ignores a naturally short task tree but keeps primary pane regressions", () => {
  const projectManagementPaneGapRows = new Function(
    "maxNumber",
    "projectManagementSampleRef",
    `${nodeWebObserveAnalyzerProjectSource()}; return projectManagementPaneGapRows;`,
  )(
    (values: unknown[]) => Math.max(0, ...values.map((value) => Number(value ?? 0))),
    (sample: Record<string, unknown>) => ({ seq: sample.seq ?? null }),
  ) as (samples: unknown[]) => { actionable: unknown[]; ignored: unknown[] };

  const pane = (name: string, bottomGapPx: number, bottomGapRatio: number) => ({
    name,
    visible: true,
    widthPx: 300,
    heightPx: 600,
    bottomGapPx,
    bottomGapRatio,
    contentNodeCount: 4,
  });
  const sample = (paneGaps: unknown[]) => ({
    seq: 1,
    projectManagement: {
      pageKind: "project-management-mdtodo",
      selectedTaskRef: { hash: "sha256:opaque" },
      paneGaps,
    },
  });

  assert.deepEqual(
    projectManagementPaneGapRows([sample([pane("task-tree", 391, 0.618)])]),
    { actionable: [], ignored: [], valuesRedacted: true },
  );
  assert.equal(
    projectManagementPaneGapRows([sample([pane("task-detail", 391, 0.618)])]).actionable.length,
    1,
  );
  assert.equal(
    projectManagementPaneGapRows([sample([
      pane("task-tree", 240, 0.4),
      pane("task-detail", 180, 0.3),
    ])]).actionable.length,
    1,
  );
});

test("MDTODO command readiness accepts a selected mobile task when the hidden tree has no visible rows", async () => {
  const source = nodeWebObserveRunnerControlSource();
  const makeReady = (snapshot: Record<string, unknown>) => new Function(
    "projectManagementCommandSnapshot",
    "safeUrlPath",
    "currentPageUrl",
    "page",
    `${source}; return waitForProjectManagementCommandReady;`,
  )(
    async () => snapshot,
    () => String(snapshot.path ?? ""),
    () => `https://hwlab.pikapython.com${String(snapshot.path ?? "")}`,
    { waitForTimeout: async () => undefined },
  ) as (options: { timeoutMs: number }) => Promise<{ ok: boolean; reason: string }>;

  const selectedMobileTask = {
    path: "/projects/mdtodo/sources/source/files/file/tasks/R1",
    pageKind: "project-management-mdtodo",
    sourceCount: 3,
    fileCount: 18,
    taskCount: 0,
    selectedTaskRef: { hash: "sha256:opaque" },
    taskBodyVisible: true,
    launchButtonVisible: true,
  };
  assert.equal((await makeReady(selectedMobileTask)({ timeoutMs: 1 })).ok, true);

  const unselectedRoot = {
    path: "/projects/mdtodo",
    pageKind: "project-management-mdtodo",
    sourceCount: 3,
    fileCount: 18,
    taskCount: 0,
  };
  assert.equal((await makeReady(unselectedRoot)({ timeoutMs: 1 })).ok, false);
});
