import { expect, test, beforeEach, afterEach } from "vitest";
import { screen, waitFor, within } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { Routes, Route } from "react-router-dom";
import { http, HttpResponse } from "msw";
import i18n from "../i18n";
import { server } from "../test/server";
import { renderApp } from "../test/render";
import { AppShell } from "./app-shell";

beforeEach(async () => {
  await i18n.changeLanguage("en");
});

afterEach(async () => {
  await i18n.changeLanguage("en");
});

function tree() {
  return (
    <Routes>
      <Route element={<AppShell />}>
        <Route path="/objects" element={<div>objects outlet</div>} />
        <Route path="/fields" element={<div>fields outlet</div>} />
      </Route>
      <Route path="/login" element={<div>login page</div>} />
    </Routes>
  );
}

test("shows active and disabled nav and renders the outlet", async () => {
  renderApp(tree(), { route: "/objects" });
  expect(await screen.findByText("objects outlet")).toBeInTheDocument();
  expect(screen.getByRole("link", { name: /objects/i })).toBeInTheDocument();
  // fields and search are now links
  expect(screen.getByRole("link", { name: /search/i })).toBeInTheDocument();
  expect(screen.getByRole("link", { name: /fields/i })).toBeInTheDocument();
});

test("renders a skip link targeting the focusable main region", async () => {
  renderApp(tree(), { route: "/objects" });
  await screen.findByText("objects outlet");

  expect(screen.getByRole("link", { name: /skip to content/i })).toHaveAttribute(
    "href",
    "#main-content",
  );

  const main = document.getElementById("main-content");
  expect(main).toBeTruthy();
  expect(main?.tabIndex).toBe(-1);
});

test("moves focus to the main region on route change", async () => {
  renderApp(tree(), { route: "/objects" });
  await screen.findByText("objects outlet");

  // Initial mount must NOT steal focus to main.
  expect(document.activeElement).not.toBe(document.getElementById("main-content"));

  await userEvent.click(screen.getByRole("link", { name: /fields/i }));
  await screen.findByText("fields outlet");

  await waitFor(() =>
    expect(document.activeElement).toBe(document.getElementById("main-content")),
  );
});

test("language switch toggles to Swedish", async () => {
  renderApp(tree(), { route: "/objects" });
  await userEvent.click(await screen.findByRole("button", { name: "SV" }));
  await waitFor(() => expect(screen.getByText("Föremål")).toBeInTheDocument());
});

test("signs out via the user menu and navigates to /login", async () => {
  let loggedOut = false;
  server.use(
    http.post("/api/admin/logout", () => {
      loggedOut = true;
      return new HttpResponse(null, { status: 204 });
    }),
  );

  renderApp(tree(), { route: "/objects" });

  // The user menu trigger shows the signed-in email (from /api/admin/me).
  const trigger = await screen.findByRole("button", { name: /editor@example.com/ });
  await userEvent.click(trigger);

  // Menu content renders in a portal on document.body.
  const menu = within(document.body);
  const signOut = await menu.findByText("Sign out");
  await userEvent.click(signOut);

  await waitFor(() => expect(loggedOut).toBe(true));
  // Sign-out replaces the route with /login.
  expect(await screen.findByText("login page")).toBeInTheDocument();
});
