import { expect, test } from "vitest";
import { screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { http, HttpResponse } from "msw";
import { Routes, Route } from "react-router-dom";
import { server } from "../test/server";
import { renderApp } from "../test/render";
import { AuthoritiesPage } from "./authorities-page";

function tree() {
  return (
    <Routes>
      <Route path="/authorities/:kind" element={<AuthoritiesPage />} />
    </Routes>
  );
}

test("lists authorities for the kind and creates one", async () => {
  let body: unknown;
  server.use(
    http.post("/api/admin/authorities", async ({ request }) => {
      body = await request.json();
      return HttpResponse.json({ id: "a-c" }, { status: 201 });
    }),
  );
  renderApp(tree(), { route: "/authorities/person" });
  expect(await screen.findByText("Ada Lovelace")).toBeInTheDocument();
  await userEvent.type(screen.getByLabelText(/^label$/i), "Carl von Linné");
  await userEvent.click(screen.getByRole("button", { name: /create/i }));
  await waitFor(() => expect((body as { kind: string })?.kind).toBe("person"));
  expect((body as { labels: { label: string }[] }).labels[0].label).toBe("Carl von Linné");
});

test("kind tabs link to the other kinds", async () => {
  renderApp(tree(), { route: "/authorities/person" });
  expect(await screen.findByRole("link", { name: /place/i })).toHaveAttribute("href", "/authorities/place");
});

test("aria-current marks the active kind link", async () => {
  renderApp(tree(), { route: "/authorities/person" });
  expect(await screen.findByRole("link", { name: /^person$/i })).toHaveAttribute("aria-current", "page");
  expect(screen.getByRole("link", { name: /^place$/i })).not.toHaveAttribute("aria-current");
});

test("create without EN label shows required alert and does not POST", async () => {
  let posted = false;
  server.use(
    http.post("/api/admin/authorities", () => {
      posted = true;
      return HttpResponse.json({ id: "a-x" }, { status: 201 });
    }),
  );
  renderApp(tree(), { route: "/authorities/person" });
  expect(await screen.findByText("Ada Lovelace")).toBeInTheDocument();
  await userEvent.click(screen.getByRole("button", { name: /create/i }));
  expect(screen.getByRole("alert")).toBeInTheDocument();
  expect(posted).toBe(false);
});

test("authorities endpoint error shows loadError", async () => {
  server.use(
    http.get("/api/admin/authorities", () => new HttpResponse(null, { status: 500 })),
  );
  renderApp(tree(), { route: "/authorities/person" });
  expect(await screen.findByText(/could not load/i)).toBeInTheDocument();
});

test("unknown kind redirects to person list", async () => {
  renderApp(tree(), { route: "/authorities/banana" });
  expect(await screen.findByText("Ada Lovelace")).toBeInTheDocument();
});

test("authorities render sorted by label", async () => {
  server.use(
    http.get("/api/admin/authorities", () =>
      HttpResponse.json([
        { id: "a-zoe", kind: "person", external_uri: null, labels: [{ lang: "en", label: "Zoe" }] },
        { id: "a-adam", kind: "person", external_uri: null, labels: [{ lang: "en", label: "Adam" }] },
      ]),
    ),
  );
  renderApp(tree(), { route: "/authorities/person" });
  expect(await screen.findByText("Adam")).toBeInTheDocument();
  const items = screen.getAllByRole("listitem");
  const texts = items.map((item) => item.textContent ?? "");
  const adam = texts.findIndex((text) => text.includes("Adam"));
  const zoe = texts.findIndex((text) => text.includes("Zoe"));
  expect(adam).toBeLessThan(zoe);
});

test("filter narrows the authority list", async () => {
  server.use(
    http.get("/api/admin/authorities", () =>
      HttpResponse.json([
        { id: "a-ada", kind: "person", external_uri: null, labels: [{ lang: "en", label: "Ada Lovelace" }] },
        { id: "a-grace", kind: "person", external_uri: null, labels: [{ lang: "en", label: "Grace Hopper" }] },
      ]),
    ),
  );
  renderApp(tree(), { route: "/authorities/person" });
  expect(await screen.findByText("Ada Lovelace")).toBeInTheDocument();
  expect(screen.getByText("Grace Hopper")).toBeInTheDocument();
  await userEvent.type(screen.getByRole("textbox", { name: /filter/i }), "grace");
  expect(screen.getByText("Grace Hopper")).toBeInTheDocument();
  expect(screen.queryByText("Ada Lovelace")).not.toBeInTheDocument();
});

test("create posts the entered external_uri", async () => {
  let body: unknown;
  server.use(
    http.post("/api/admin/authorities", async ({ request }) => {
      body = await request.json();
      return HttpResponse.json({ id: "a-c" }, { status: 201 });
    }),
  );
  renderApp(tree(), { route: "/authorities/person" });
  expect(await screen.findByText("Ada Lovelace")).toBeInTheDocument();
  await userEvent.type(screen.getByLabelText(/^label$/i), "Carl von Linné");
  await userEvent.type(screen.getByLabelText(/external uri/i), "https://viaf.org/456");
  await userEvent.click(screen.getByRole("button", { name: /create/i }));
  await waitFor(() =>
    expect((body as { external_uri: string })?.external_uri).toBe("https://viaf.org/456"),
  );
});

test("read row shows its external_uri as a link", async () => {
  server.use(
    http.get("/api/admin/authorities", () =>
      HttpResponse.json([
        { id: "a-ada", kind: "person", external_uri: "https://viaf.org/123", labels: [{ lang: "en", label: "Ada Lovelace" }] },
      ]),
    ),
  );
  renderApp(tree(), { route: "/authorities/person" });
  expect(await screen.findByText("Ada Lovelace")).toBeInTheDocument();
  expect(screen.getByRole("link", { name: /viaf\.org/ })).toBeInTheDocument();
});
