import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { render, screen, fireEvent, waitFor } from "@testing-library/react";
import SearchPage from "./page";

const ORIGINAL_ENV = process.env;

beforeEach(() => {
  process.env = { ...ORIGINAL_ENV, NEXT_PUBLIC_MAIL_TENANT_SLUG: "acme" };
});

afterEach(() => {
  process.env = ORIGINAL_ENV;
  vi.restoreAllMocks();
});

function mockSearchResponse(hits: unknown[]) {
  vi.stubGlobal(
    "fetch",
    vi.fn().mockResolvedValue(new Response(JSON.stringify({ hits }), { status: 200 }))
  );
}

// Akzeptanzkriterium 1: Eingabe liefert Trefferliste mit hervorgehobenen
// Suchbegriffen im Snippet.
describe("SearchPage — Trefferliste mit Hervorhebung", () => {
  it("rendert Treffer mit <mark> um den hervorgehobenen Suchbegriff", async () => {
    mockSearchResponse([
      {
        messageId: "msg-1",
        subjectSnippet: "Quartalsbericht ⦃⦃Umsatz⦄⦄",
        bodySnippet: "hoher ⦃⦃Umsatz⦄⦄ im dritten Quartal",
        score: 100,
      },
    ]);

    render(<SearchPage />);
    fireEvent.change(screen.getByLabelText("Suchbegriff"), { target: { value: "Umsatz" } });
    fireEvent.submit(screen.getByLabelText("Suchbegriff").closest("form")!);

    await waitFor(() => {
      const marks = document.querySelectorAll("mark");
      expect(marks.length).toBeGreaterThan(0);
    });
    const marks = Array.from(document.querySelectorAll("mark")).map((m) => m.textContent);
    expect(marks).toContain("Umsatz");
  });

  // Akzeptanzkriterium 3: leere Ergebnisse zeigen verständlichen Hinweis
  // statt leerer Fläche.
  it("zeigt bei leerem Ergebnis einen verständlichen Hinweis", async () => {
    mockSearchResponse([]);

    render(<SearchPage />);
    fireEvent.change(screen.getByLabelText("Suchbegriff"), { target: { value: "nichts-vorhanden" } });
    fireEvent.submit(screen.getByLabelText("Suchbegriff").closest("form")!);

    await waitFor(() => {
      expect(screen.getByRole("status")).toHaveTextContent("Keine Treffer");
    });
  });

  // Pflichtprüfung 3: Sonderzeichen in der Suchanfrage bringen die Anzeige
  // nicht zum Absturz.
  it("wirft bei Sonderzeichen in der Suchanfrage keinen Fehler", async () => {
    mockSearchResponse([
      {
        messageId: "msg-2",
        subjectSnippet: `⦃⦃<script>alert(1)</script>⦄⦄ & "Zitat" 日本語`,
        bodySnippet: "",
        score: 1,
      },
    ]);

    render(<SearchPage />);
    fireEvent.change(screen.getByLabelText("Suchbegriff"), {
      target: { value: '"<script>" OR -Ümläüt 日本語' },
    });
    expect(() => fireEvent.submit(screen.getByLabelText("Suchbegriff").closest("form")!)).not.toThrow();

    await waitFor(() => {
      expect(document.querySelectorAll("mark").length).toBeGreaterThan(0);
    });
    // Kein <script>-Element im DOM entstanden (kein dangerouslySetInnerHTML,
    // Manticore-Highlight wird als reiner Text gerendert, nicht als HTML).
    expect(document.querySelectorAll("script").length).toBe(0);
  });

  // Akzeptanzkriterium 2: Klick auf Treffer öffnet die zugehörige Mail
  // direkt an der Fundstelle — hier geprüft über den erzeugten Link, da
  // die vollständige Mail-Ansicht (Inhaltsabruf per API) INT-01 vorbehalten
  // ist.
  it("verlinkt jeden Treffer auf die Mail-Detailseite mit Fundstellen-Anker", async () => {
    mockSearchResponse([
      { messageId: "msg-3", subjectSnippet: "Betreff", bodySnippet: "Text", score: 1 },
    ]);

    render(<SearchPage />);
    fireEvent.change(screen.getByLabelText("Suchbegriff"), { target: { value: "Betreff" } });
    fireEvent.submit(screen.getByLabelText("Suchbegriff").closest("form")!);

    await waitFor(() => {
      const link = screen.getByRole("link");
      expect(link.getAttribute("href")).toMatch(/^\/mail\/msg-3\?.*#fundstelle$/);
    });
  });
});
