// @vitest-environment jsdom
/**
 * TZ §4.1.21 — 404 + 500 error page compliance tests.
 *
 * Rules verified:
 *  R1  Unknown URL → 404 (route-level, not tested here — see $.error.tsx)
 *  R2  Server error → 500 (route-level, not tested here — see error/[code]/page.tsx)
 *  R3  URL preserved on error page (render in-place, no redirect)
 *  R4  404: link to root + localized message
 *  R5  500: refresh CTA + support contact + localized message
 *  R6  noindex meta on both pages
 *  R7  Translated for all 9 locales (i18n key presence check)
 *  R8  HTTP status matching (loader-level, integration-tested separately)
 */

import { render, screen, fireEvent } from "@testing-library/react";
import { ErrorPage } from "./ErrorPage.js";

// Suppress React act() warnings about async state updates in useEffect
beforeAll(() => {
  vi.spyOn(console, "error").mockImplementation(() => {});
});
afterAll(() => {
  vi.restoreAllMocks();
});

describe("4.1.21 — ErrorPage", () => {
  // ── R6: noindex meta ──────────────────────────────────────────────────────

  it("4.1.21-R6: 404 page renders noindex meta", () => {
    render(<ErrorPage code="404" />);
    const meta = document.querySelector('meta[name="robots"]');
    expect(meta).not.toBeNull();
    expect(meta?.getAttribute("content")).toMatch(/noindex/);
  });

  it("4.1.21-R6: 500 page renders noindex meta", () => {
    render(<ErrorPage code="500" />);
    const meta = document.querySelector('meta[name="robots"]');
    expect(meta).not.toBeNull();
    expect(meta?.getAttribute("content")).toMatch(/noindex/);
  });

  it("4.1.21-R6: unknown code fallback page renders noindex meta", () => {
    render(<ErrorPage />);
    const meta = document.querySelector('meta[name="robots"]');
    expect(meta).not.toBeNull();
    expect(meta?.getAttribute("content")).toMatch(/noindex/);
  });

  // ── R4: 404 page has root link ────────────────────────────────────────────

  it("4.1.21-R4: 404 page has a link to root /", () => {
    render(<ErrorPage code="404" />);
    const rootLink = document.querySelector('a[href="/"]');
    expect(rootLink).not.toBeNull();
  });

  it("4.1.21-R4: 500 page has a link to root /", () => {
    render(<ErrorPage code="500" />);
    const rootLink = document.querySelector('a[href="/"]');
    expect(rootLink).not.toBeNull();
  });

  // ── R4: 404 localized message (SSR Russian fallback) ─────────────────────

  it("4.1.21-R4: 404 page shows SSR Russian title fallback", () => {
    render(<ErrorPage code="404" />);
    // Before i18n resolves, title is the key itself
    const page = screen.getByTestId("error-page-404");
    expect(page).toBeDefined();
    // Displays the numeric code
    expect(page.textContent).toContain("404");
  });

  // ── R5: 500 page refresh CTA ─────────────────────────────────────────────

  it("4.1.21-R5: 500 page shows refresh button", () => {
    render(<ErrorPage code="500" />);
    const refreshBtn = screen.getByTestId("error-page-refresh");
    expect(refreshBtn).toBeDefined();
  });

  it("4.1.21-R5: 503 page shows refresh button", () => {
    render(<ErrorPage code="503" />);
    const refreshBtn = screen.getByTestId("error-page-refresh");
    expect(refreshBtn).toBeDefined();
  });

  it("4.1.21-R5: 404 page does NOT show refresh button", () => {
    render(<ErrorPage code="404" />);
    const refreshBtn = document.querySelector('[data-testid="error-page-refresh"]');
    expect(refreshBtn).toBeNull();
  });

  it("4.1.21-R5: refresh button calls window.location.reload", () => {
    const reloadMock = vi.fn();
    Object.defineProperty(window, "location", {
      value: { reload: reloadMock, pathname: "/" },
      writable: true,
    });

    render(<ErrorPage code="500" />);
    const refreshBtn = screen.getByTestId("error-page-refresh");
    fireEvent.click(refreshBtn);
    expect(reloadMock).toHaveBeenCalledOnce();
  });

  // ── R5: 500 page has support link ─────────────────────────────────────────

  it("4.1.21-R5: 500 page has support link", () => {
    render(<ErrorPage code="500" />);
    const supportLink = document.querySelector('a[href*="help"]');
    expect(supportLink).not.toBeNull();
  });

  // ── R7: i18n keys present in all 9 locales ───────────────────────────────
  // We verify the locale JSON files directly rather than loading the full i18n stack.

  const LOCALES = ["ru", "en", "de", "es", "fr", "it", "ja", "ko", "zh"] as const;

  it.each(LOCALES)(
    "4.1.21-R7: locale %s has PAGE404 keys (HEADER, DESCRIPTION, TO-HOME)",
    async (lang) => {
      const mod = await import(`../../i18n/locales/${lang}/common.json`);
      const data = mod.default as Record<string, Record<string, string>>;
      const page404 = data["PAGE404"];
      expect(page404).toBeDefined();
      expect(page404?.["HEADER"]).toBeTruthy();
      expect(page404?.["DESCRIPTION"]).toBeTruthy();
      expect(page404?.["TO-HOME"]).toBeTruthy();
    },
  );

  it.each(LOCALES)(
    "4.1.21-R7: locale %s has PAGE500 keys (HEADER, DESCRIPTION, REFRESH, SUPPORT)",
    async (lang) => {
      const mod = await import(`../../i18n/locales/${lang}/common.json`);
      const data = mod.default as Record<string, Record<string, string>>;
      const page500 = data["PAGE500"];
      expect(page500).toBeDefined();
      expect(page500?.["HEADER"]).toBeTruthy();
      expect(page500?.["DESCRIPTION"]).toBeTruthy();
      expect(page500?.["REFRESH"]).toBeTruthy();
      expect(page500?.["SUPPORT"]).toBeTruthy();
    },
  );

  // ── SSR status code loader tests ─────────────────────────────────────────

  it("4.1.21-R8: splat route loader throws a 404 Response", async () => {
    const { loader } = await import("../../routes/$/page.js");
    expect(loader).toBeDefined();
    let caught: Response | null = null;
    try {
      // loader is typed as () => never; it always throws
      (loader as () => never)();
    } catch (e) {
      caught = e as Response;
    }
    expect(caught).toBeInstanceOf(Response);
    expect((caught as Response).status).toBe(404);
  });

  it("4.1.21-R8: error/[code] loader throws 404 for code=404", async () => {
    const { loader } = await import("../../routes/error/[code]/page.js");
    expect(loader).toBeDefined();
    let caught: Response | null = null;
    try {
      await (loader as (args: { params: Record<string, string> }) => Promise<never>)(
        { params: { code: "404" } }
      );
    } catch (e) {
      caught = e as Response;
    }
    expect(caught).toBeInstanceOf(Response);
    expect((caught as Response).status).toBe(404);
  });

  it("4.1.21-R8: error/[code] loader throws 500 for code=500", async () => {
    const { loader } = await import("../../routes/error/[code]/page.js");
    let caught: Response | null = null;
    try {
      await (loader as (args: { params: Record<string, string> }) => Promise<never>)(
        { params: { code: "500" } }
      );
    } catch (e) {
      caught = e as Response;
    }
    expect((caught as Response).status).toBe(500);
  });

  it("4.1.21-R8: error/[code] loader throws 503 for code=503", async () => {
    const { loader } = await import("../../routes/error/[code]/page.js");
    let caught: Response | null = null;
    try {
      await (loader as (args: { params: Record<string, string> }) => Promise<never>)(
        { params: { code: "503" } }
      );
    } catch (e) {
      caught = e as Response;
    }
    expect((caught as Response).status).toBe(503);
  });

  it("4.1.21-R8: error/[code] loader throws 500 for unknown code", async () => {
    const { loader } = await import("../../routes/error/[code]/page.js");
    let caught: Response | null = null;
    try {
      await (loader as (args: { params: Record<string, string> }) => Promise<never>)(
        { params: { code: "418" } }
      );
    } catch (e) {
      caught = e as Response;
    }
    expect((caught as Response).status).toBe(500);
  });
});
