/**
 * #69 lot 4 — the web client on a phone viewport.
 *
 * Two guards:
 *
 *  1. **No desktop-only shell.** The mobile brief assumed the web client had to
 *     trade docks / floating windows / a layout grid for a vertical stack. It
 *     never had them: #13 shipped it as a single-column read-only surface. That
 *     is easy to regress by reaching for a desktop component, so these tests pin
 *     the boundary — the web client mounts no `layout-*` grid and no dock.
 *  2. **The agent cell is phone-drivable** — it carries the key toolbar, since a
 *     virtual keyboard alone cannot send Ctrl-C/Esc/Tab/arrows.
 */
import { describe, it, expect, beforeEach } from "vitest";
import { fireEvent, render, screen } from "@testing-library/react";

import type { Gateways } from "@/ports";
import { DIProvider } from "@/app/di";
import { createMockGateways, MockWorkStateGateway } from "@/adapters/mock";
import { WebSession } from "@/adapters/http";
import type { FetchLike } from "@/adapters/http/httpInvoker";
import type { FlagStore } from "@/adapters/http/webSession";
import { WebApp } from "./WebApp";

/** Widths we care about: a small phone, and a large one. */
const PHONE_WIDTH = 360;

function memStore(): FlagStore {
  const map = new Map<string, string>();
  return {
    getItem: (k) => map.get(k) ?? null,
    setItem: (k, v) => void map.set(k, v),
    removeItem: (k) => void map.delete(k),
  };
}

const okFetch: FetchLike = async () => ({
  ok: true,
  status: 200,
  json: async () => ({ ok: true }),
  text: async () => "{}",
});

async function seededGateways(): Promise<Gateways> {
  const gateways = createMockGateways();
  const project = await gateways.project.createProject("Demo", "/srv/demo");
  (gateways.workState as MockWorkStateGateway)._setProjectWorkState(project.id, {
    agents: [
      { agentId: "a1", name: "Archi", profileId: "p1", busy: { state: "idle" }, tickets: [] },
    ],
    conversations: [],
  });
  return gateways;
}

async function renderPairedWebApp() {
  const session = new WebSession({ baseUrl: "https://h", fetchImpl: okFetch, store: memStore() });
  session.markPaired();
  render(
    <DIProvider gateways={await seededGateways()}>
      <WebApp session={session} />
    </DIProvider>,
  );
}

beforeEach(() => {
  // jsdom defaults to 1024px; pin a phone width so anything reading the viewport
  // sees a phone. (Tailwind breakpoints are CSS-only and unresolved in jsdom —
  // hence the structural assertions below rather than computed-style ones.)
  window.innerWidth = PHONE_WIDTH;
  window.dispatchEvent(new Event("resize"));
});

describe("web client on a phone viewport", () => {
  it("mounts no desktop-only layout grid, split or dock", async () => {
    await renderPairedWebApp();
    await screen.findByText("Projets");

    for (const testId of ["layout-grid", "layout-grid-container", "layout-split", "layout-tabs"]) {
      expect(screen.queryByTestId(testId)).toBeNull();
    }
  });

  it("stacks the workspace vertically in a single scroll container", async () => {
    await renderPairedWebApp();

    const workspace = await screen.findByTestId("web-workspace");
    expect(workspace.className).toContain("flex-col");
    expect(workspace.className).toContain("overflow-y-auto");
  });

  it("gives the agent cell a key toolbar so a phone can send Ctrl-C/Esc/arrows", async () => {
    await renderPairedWebApp();

    fireEvent.click(await screen.findByText("Demo"));
    await screen.findByTestId("web-workstate");
    fireEvent.click(screen.getByRole("button", { name: "Ouvrir" }));

    expect(await screen.findByTestId("web-agent-cell")).toBeTruthy();
    expect(screen.getByTestId("terminal-key-bar")).toBeTruthy();
  });

  it("opens the Panneaux menu as a full-screen sheet at 360px (no fixed-width overflow)", async () => {
    await renderPairedWebApp();

    fireEvent.click(await screen.findByText("Demo"));
    fireEvent.click(await screen.findByRole("button", { name: "Panneaux : Travail" }));

    const sheet = await screen.findByRole("dialog", { name: "Panneaux" });
    // Full-viewport overlay (`fixed inset-0`), not a fixed-width dropdown that
    // could overflow a 360px screen.
    expect(sheet.className).toContain("fixed");
    expect(sheet.className).toContain("inset-0");
  });

  it("sizes the agent cell against the dynamic viewport, not a fixed 256px", async () => {
    await renderPairedWebApp();

    fireEvent.click(await screen.findByText("Demo"));
    await screen.findByTestId("web-workstate");
    fireEvent.click(screen.getByRole("button", { name: "Ouvrir" }));

    // `dvh` (not `vh`/`h-64`) is what survives a mobile URL bar collapsing.
    // Match exact class tokens: `min-h-64` legitimately contains "h-64".
    const cell = await screen.findByTestId("web-agent-cell");
    const classes = cell.className.split(/\s+/);
    expect(classes).toContain("h-[60dvh]");
    expect(classes).not.toContain("h-64");
  });
});
