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

test("the main chat screen renders each recent project as a titled chat section", async () => {
  const panel = await readFile(new URL("./ChatsPanel.jsx", import.meta.url), "utf8");

  assert.match(panel, /groupedProjects\.map\(\(project\) => <section[^>]*className="thread-section project-thread-preview"/);
  assert.match(panel, /<h3>\{project\.label\}<\/h3>/);
  assert.match(panel, /visibleProjectThreads\(project\.threads, projectRowLimits\[project\.key\]\)/);
  assert.match(panel, /nextProjectThreadLimit\(current\[projectKey\], total\)/);
  assert.match(panel, /t\("chats\.loadMore"\)/);
});

test("project folders start open and toggle only their own inline chat rows", async () => {
  const [panel, icons] = await Promise.all([
    readFile(new URL("./ChatsPanel.jsx", import.meta.url), "utf8"),
    readFile(new URL("../../shared/ActionIcons.jsx", import.meta.url), "utf8")
  ]);

  assert.match(icons, /export function FolderOpenIcon/);
  assert.match(icons, /export function FolderClosedIcon[\s\S]*<FolderClosed/);
  assert.match(icons, /export function ChatClassicViewIcon[\s\S]*<List /);
  assert.match(icons, /export function ChatGroupedViewIcon[\s\S]*<ListCollapse/);
  assert.match(panel, /const \[collapsedProjects, setCollapsedProjects\] = useState\(\{\}\)/);
  assert.match(panel, /\[projectKey\]: !current\[projectKey\]/);
  assert.match(panel, /setProjectRowLimits\(\(current\) => \(\{ \.\.\.current, \[projectKey\]: undefined \}\)\)/);
  assert.match(panel, /className="project-button" data-appearance="plain" aria-expanded=\{!collapsedProjects\[project\.key\]\}/);
  assert.match(panel, /collapsedProjects\[project\.key\] \? <FolderClosedIcon[^>]*> : <FolderOpenIcon/);
  assert.match(panel, /toggleProject\(project\.key\);/);
  assert.match(panel, /\{!collapsedProjects\[project\.key\] && <>/);
  assert.match(panel, /groupedView && groupedProjects\.map/);
});

test("the classic main screen is the default and the top-right control persists grouped view", async () => {
  const [panel, css] = await Promise.all([
    readFile(new URL("./ChatsPanel.jsx", import.meta.url), "utf8"),
    readFile(new URL("./chats.css", import.meta.url), "utf8")
  ]);

  assert.match(panel, /readChatViewPreference\(globalThis\.localStorage\) === "grouped"/);
  assert.match(panel, /className="projects-heading"/);
  assert.match(panel, /className="chat-view-toggle"/);
  assert.match(panel, /<div className="thread-list"[^>]*>\s*\{!searching && !activeProject && !archived && <button[^>]*className="chat-view-toggle"/);
  assert.match(css, /\.chats-panel \.thread-list\{position:relative\}/);
  assert.doesNotMatch(panel, /setViewToggleTop|viewToggleTop|ResizeObserver/);
  assert.match(css, /\.chat-view-toggle\{[^}]*position:absolute[^}]*top:14px[^}]*right:14px[^}]*z-index:3/);
  assert.match(panel, /className="thread-section pinned-threads chat-list-top-section"/);
  assert.match(panel, /className=\{`projects-section\$\{pinned\.length > 0 \? "" : " chat-list-top-section"\}`\}/);
  assert.match(css, /\.chat-list-top-section>\:is\(h2,\.projects-heading\)\{[^}]*min-height:48px[^}]*align-items:center[^}]*padding-right:56px/);
  assert.match(panel, /groupedView \? <ChatGroupedViewIcon[^>]*> : <ChatClassicViewIcon/);
  assert.doesNotMatch(panel, /className="chat-view-toggle"[^>]*>\s*\{t\(/);
  assert.match(panel, /writeChatViewPreference\(globalThis\.localStorage, nextGroupedView \? "grouped" : "classic"\)/);
  assert.match(panel, /!groupedView && projects\.map\(\(project\) => <button[^>]*className="project-card"/);
  assert.match(panel, /const \[selectedProject, setSelectedProject\] = useState\(""\)/);
  assert.match(panel, /className="project-chat-composer"/);
});

test("the grouped recent-chat section contains only projectless chats while classic view keeps its recent list", async () => {
  const panel = await readFile(new URL("./ChatsPanel.jsx", import.meta.url), "utf8");

  assert.match(panel, /const recent = archived \? threads : groupedView \? projectlessThreads\(threads\)\.filter\(\(thread\) => !thread\.pinned\) : recentThreads\(threads\)\.filter/);
  assert.match(panel, /const pinned = archived/);
  assert.match(panel, /!archived && pinned\.length > 0/);
  assert.match(panel, /<h2>\{t\("chats\.recent"\)\}<\/h2>/);
});

test("both views place pinned chats above project folders while grouped lists avoid duplicates", async () => {
  const panel = await readFile(new URL("./ChatsPanel.jsx", import.meta.url), "utf8");

  assert.match(panel, /const groupedProjects = projects\.map\(\(project\) => \(\{ \.\.\.project, threads: project\.threads\.filter\(\(thread\) => !thread\.pinned\) \}\)\)/);
  assert.match(panel, /groupedView \? projectlessThreads\(threads\)\.filter\(\(thread\) => !thread\.pinned\) : recentThreads/);
  assert.match(panel, /!archived && pinned\.length > 0/);
  assert.doesNotMatch(panel, /groupedView && pinned\.length > 0/);
  assert.match(panel, /groupedView && groupedProjects\.map/);
  assert.ok(panel.indexOf("!archived && pinned.length > 0") < panel.indexOf("!searching && !activeProject && !archived && <section className={`projects-section"));
});

test("light grouped view keeps the view icon, project headings, and load-more actions unshaded", async () => {
  const [panel, baseCss] = await Promise.all([
    readFile(new URL("./ChatsPanel.jsx", import.meta.url), "utf8"),
    readFile(new URL("../../shared/styles/base.css", import.meta.url), "utf8")
  ]);

  assert.match(baseCss, /button\[data-appearance="plain"\][^{]*\{[^}]*background:transparent!important[^}]*border-color:transparent!important[^}]*box-shadow:none!important/);
  assert.match(panel, /className="chat-view-toggle"[^>]*data-appearance="plain"/);
  assert.match(panel, /className="project-button" data-appearance="plain"/);
  assert.match(panel, /className="project-load-more" data-appearance="plain"/);
  const css = await readFile(new URL("./chats.css", import.meta.url), "utf8");
  assert.match(css, /\.chat-view-toggle:is\(:hover,:active,:focus-visible\)\{background:transparent;border-color:transparent;box-shadow:none/);
});

test("chat rows use a flat compact surface while preserving the mobile touch target", async () => {
  const [panel, css] = await Promise.all([
    readFile(new URL("./ChatsPanel.jsx", import.meta.url), "utf8"),
    readFile(new URL("./chats.css", import.meta.url), "utf8")
  ]);

  assert.match(panel, /className=\{activeThreadId[^>]*data-appearance="plain"/);
  assert.match(css, /\.thread-section-list\{gap:0\}/);
  assert.match(css, /\.thread-row,\.thread-row\.active\{border:0;background:transparent;box-shadow:none\}/);
  assert.match(css, /\.project-list--grouped\{[^}]*gap:0\}/);
  assert.match(css, /\.project-thread-preview\{gap:0\}/);
  assert.match(css, /\.project-list--grouped \.project-button\{min-height:40px;padding:0 2px\}/);
  assert.match(css, /\.thread-row\{min-height:40px;padding:0 2px\}/);
  assert.match(css, /@media\(max-width:640px\)\{[\s\S]*?:is\(\.thread-row,\.project-list--grouped \.project-button\)\{min-height:48px\}/);
});
