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

import {
  calculateVirtualGridColumnCount,
  calculateVirtualGridRowWindowLimit,
  chunkVirtualGridItems,
} from "../src/components/common/virtual-grid.ts";

const viewSource = readSource("../src/views/agents/AgentRunsView.vue");
const inspectorSource = readSource("../src/components/agents/AgentRunInspector.vue");
const inspectorPanelSource = readSource("../src/components/console/InspectorPanel.vue");
const mindMapSource = readSource("../src/components/agents/AgentRunMindMap.vue");
const virtualGridSource = readSource("../src/components/common/VirtualGrid.vue");
const styleSource = readSource("../src/styles/agent-observer.css");

test("VirtualGrid calculates three responsive column tiers", () => {
  assert.equal(calculateVirtualGridColumnCount(1920, 360, 3, 10), 3);
  assert.equal(calculateVirtualGridColumnCount(960, 360, 3, 10), 2);
  assert.equal(calculateVirtualGridColumnCount(390, 360, 3, 10), 1);
});

test("VirtualGrid chunks stable rows and bounds the rendered row window", () => {
  const items = Array.from({ length: 10 }, (_, index) => ({ id: `run-${index}` }));
  const rows = chunkVirtualGridItems(items, 3, (item) => item.id);
  assert.deepEqual(rows.map((row) => row.items.length), [3, 3, 3, 1]);
  assert.deepEqual(rows.map((row) => row.startIndex), [0, 3, 6, 9]);
  assert.equal(rows[0]?.key, "0:run-0|run-1|run-2");
  assert.equal(calculateVirtualGridRowWindowLimit(12, 3), 4);
  assert.equal(calculateVirtualGridRowWindowLimit(12, 1), 12);
  assert.match(virtualGridSource, /ResizeObserver/);
  assert.match(virtualGridSource, /:window-limit="rowWindowLimit"/);
  assert.match(virtualGridSource, /role="listitem"/);
});

test("AgentRun table exposes the eight-column product contract", () => {
  const headers = [
    "状态",
    "Run / Task / Command identity",
    "Project / Workspace",
    "Agent / Profile",
    "阶段",
    "活动 / 耗时",
    "Runner / Node / Lane",
    "结果 / 错误",
  ];
  let previousIndex = -1;
  for (const header of headers) {
    const index = viewSource.indexOf(`role="columnheader">${header}`);
    assert.ok(index > previousIndex, `missing or unordered table header: ${header}`);
    previousIndex = index;
  }
});

test("AgentRun page reuses shared status, viewer and embedded inspector contracts", () => {
  assert.match(viewSource, /import StatusStrip/);
  assert.match(viewSource, /<StatusStrip :items="statusItems"/);
  assert.match(viewSource, /import VirtualGrid/);
  assert.match(viewSource, /<VirtualGrid/);
  assert.match(viewSource, /<AgentRunInspector v-if="observer\.selectedRun" embedded/);
  assert.match(inspectorSource, /import ContentViewer/);
  assert.match(inspectorSource, /mode="log"/);
  assert.match(inspectorSource, /mode="json"/);
  assert.doesNotMatch(inspectorSource, /<pre/);
  assert.match(inspectorPanelSource, /embedded\?: boolean/);
  assert.match(inspectorPanelSource, /v-if="!embedded"/);
});

test("AgentRun command bar preserves compact desktop and mobile primary controls", () => {
  assert.match(styleSource, /@media \(max-width: 1100px\)/);
  assert.match(styleSource, /agent-observer-secondary-filters \{ order: 5; flex: 1 0 100%; \}/);
  assert.match(styleSource, /@media \(max-width: 600px\)/);
  assert.match(styleSource, /agent-observer-secondary-filters,[\s\S]*agent-observer-density \{ display: none; \}/);
  assert.match(styleSource, /agent-observer-filter-trigger \{ grid-column: 2; display: inline-flex; \}/);
  assert.match(styleSource, /agent-observer-segment \{ grid-column: 1 \/ -1; width: 100%; \}/);
  assert.match(styleSource, /@media \(prefers-reduced-motion: reduce\)/);
  assert.match(viewSource, /class="agent-observer-search"/);
  assert.match(viewSource, /class="agent-observer-primary-status"/);
  assert.match(viewSource, /class="agent-observer-segment" role="group" aria-label="视图切换"/);
  assert.match(viewSource, /class="agent-observer-stream"/);
});

test("AgentRun workspace always owns the remaining grid row", () => {
  assert.match(styleSource, /agent-observer-page \{[\s\S]*?height: 100%;/);
  assert.match(styleSource, /agent-observer-workspace \{ grid-row: -2 \/ -1;/);
});

test("AgentRun mind map always owns the flexible tree row", () => {
  assert.match(styleSource, /agent-observer-tree > \.agent-mind-map \{ grid-row: -2 \/ -1; \}/);
});

test("AgentRun mind map connects custom nodes through Vue Flow handles", () => {
  assert.match(mindMapSource, /import \{ Handle, MarkerType, Position, VueFlow/);
  assert.match(mindMapSource, /<Handle v-if="data\.row\.parentId" type="target" :position="Position\.Left" \/>/);
  assert.match(mindMapSource, /<Handle v-if="data\.row\.childCount > 0" type="source" :position="Position\.Right" \/>/);
  assert.match(styleSource, /agent-mind-map-flow \.vue-flow__handle/);
  assert.match(styleSource, /agent-mind-map-flow \.vue-flow__edge-path \{[^}]*vector-effect: non-scaling-stroke;/);
  assert.match(styleSource, /agent-mind-map-flow \.vue-flow__edge-path \{[^}]*stroke: var\(--console-graphite-500\);/);
  assert.match(styleSource, /agent-mind-map-flow \.vue-flow__arrowhead \{ fill: var\(--console-graphite-500\); \}/);
});

test("AgentRun inspector keeps chrome fixed and scrolls only its body", () => {
  assert.match(styleSource, /agent-observer-inspector \{[^}]*overflow: hidden;/);
  assert.match(styleSource, /agent-observer-inspector > \.operations-inspector-panel \{[^}]*grid-template-rows: auto auto minmax\(0, 1fr\) auto;/);
  assert.match(styleSource, /agent-observer-inspector \.operations-inspector-body \{[^}]*overflow: auto;/);
});

function readSource(relativePath: string): string {
  return readFileSync(new URL(relativePath, import.meta.url), "utf8");
}
