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

test("changes requests expose retryable fixed failures", async () => {
  const [controller, panel, app] = await Promise.all([
    readFile(new URL("./useChanges.js", import.meta.url), "utf8"),
    readFile(new URL("./ChangesPanel.jsx", import.meta.url), "utf8"),
    readFile(new URL("../../App.jsx", import.meta.url), "utf8")
  ]);

  assert.match(controller, /files\.failed/);
  assert.match(controller, /loadPatchDiffs/);
  assert.doesNotMatch(controller, /error\.message/);
  assert.match(panel, /role="alert"/);
  assert.match(panel, /pending/);
  assert.match(app, /error=\{changes\.error\}/);
  assert.match(app, /pending=\{changes\.pending\}/);
});

test("changed files open in a dismissible mobile diff sheet", async () => {
  const [controller, sheet, app, css] = await Promise.all([
    readFile(new URL("./useChanges.js", import.meta.url), "utf8"),
    readFile(new URL("./ChangeDiffSheet.jsx", import.meta.url), "utf8"),
    readFile(new URL("../../App.jsx", import.meta.url), "utf8"),
    readFile(new URL("./change-sheet.css", import.meta.url), "utf8")
  ]);

  assert.match(controller, /setSheetOpen\(true\)/);
  assert.match(controller, /closeSheet/);
  assert.match(sheet, /role="dialog"/);
  assert.match(sheet, /aria-modal="true"/);
  assert.match(sheet, /change-sheet-backdrop--exit/);
  assert.match(sheet, /className="change-sheet-close"/);
  assert.match(sheet, /diff-line/);
  assert.match(app, /<ChangeDiffSheet/);
  assert.match(app, /changedFiles=\{app\.chatTransition \? \[\] : changes\.files\}/);
  assert.match(app, /onOpenChangedFile=\{changes\.open\}/);
  assert.match(controller, /open\(files\)/);
  assert.match(controller, /loadPatchDiffs\(files/);
  assert.match(controller, /setSheetFiles\(files\)/);
  assert.match(app, /files=\{changes\.sheetFiles\}/);
  assert.match(app, /diffs=\{changes\.selectedDiffs\}/);
  assert.doesNotMatch(app, /<ChangeDiffSheet[^>]*files=\{changes\.files\}/);
  assert.match(sheet, /diffs\.map/);
  assert.match(sheet, /<details[^>]*open=\{expanded\}/);
  assert.match(sheet, /onToggle/);
  assert.match(sheet, /summarizeChangePath\(diff\.path\)/);
  assert.match(sheet, /change-file-directory/);
  assert.match(sheet, /change-file-basename/);
  assert.match(sheet, /change-file-extension/);
  assert.match(sheet, /change-file-stats/);
  assert.match(sheet, /changes\.unavailable\.\$\{reason\}/);
  assert.match(sheet, /changes\.unavailable\.\$\{reason\}/);
  assert.match(sheet, /useI18n/);
  assert.equal(sheet.includes("+{diff.additions ?? 0}"), true);
  assert.equal(sheet.includes("-{diff.deletions ?? 0}"), true);
  assert.match(css, /\.change-file-directory\s*\{[^}]*text-overflow:\s*ellipsis/);
  assert.match(css, /\.change-file-extension\s*\{[^}]*flex:\s*0 0 auto/);
  assert.match(css, /\.change-file-stats\s*\{[^}]*margin-left:\s*auto/);
  assert.doesNotMatch(app, /const changesPanel/);
  assert.match(css, /\.change-sheet\s*\{[^}]*position:\s*fixed[^}]*bottom:\s*0/);
  assert.match(css, /@media\(max-width:\s*640px\)[\s\S]*\.change-sheet/);
});

test("changed files load automatically when a session opens", async () => {
  const controller = await readFile(new URL("./useChanges.js", import.meta.url), "utf8");

  assert.match(controller, /useEffect\(\(\) => \{[\s\S]*void refresh\(\)[\s\S]*\}, \[session\?\.id\]\)/);
});

test("completed file activity refreshes changed-file rows for the live patch log", async () => {
  const [conversation, app] = await Promise.all([
    readFile(new URL("../conversation/useConversation.js", import.meta.url), "utf8"),
    readFile(new URL("../../App.jsx", import.meta.url), "utf8")
  ]);

  assert.match(conversation, /name === "activity\.completed" && data\.kind === "fileChange"/);
  assert.match(conversation, /onFileActivity\(\)/);
  assert.match(app, /onFileActivity:\s*changes\.refresh/);
});

test("late Changes responses cannot replace the active chat files or diff sheet", async () => {
  const controller = await readFile(new URL("./useChanges.js", import.meta.url), "utf8");

  assert.match(controller, /const sessionRef = useRef\(session\)/);
  assert.match(controller, /sessionRef\.current = session/);
  assert.match(controller, /const requestSessionId = session\.id/);
  assert.match(controller, /isCurrentSessionRequest\(sessionRef, requestSessionId\)/);
  assert.match(controller, /if \(!isCurrentSessionRequest\(sessionRef, requestSessionId\)\) return false/);
  assert.ok(controller.indexOf("const requestSessionId = session.id") < controller.indexOf("loadChangedFiles(requestSessionId)"));
  assert.ok(controller.indexOf("const requestSessionId = session.id") < controller.indexOf("loadFileDiff(requestSessionId"));
  assert.match(controller, /dispatch\(\{ type: "diffs\.loaded", diffs \}\)/);
});
