// @vitest-environment happy-dom
import { describe, it, expect, vi, beforeEach } from "vitest";
import { render, fireEvent, waitFor, cleanup } from "@testing-library/react";
import { MemoryRouter, Routes, Route } from "react-router-dom";
import { Login } from "./Login";

vi.mock("@/auth/useAuth", () => ({ useAuth: vi.fn() }));
import { useAuth } from "@/auth/useAuth";

function setup() {
  const login = vi.fn();
  (useAuth as unknown as ReturnType<typeof vi.fn>).mockReturnValue({
    user: null,
    status: "unauthenticated",
    login,
    logout: vi.fn(),
    refresh: vi.fn(),
  });
  return { login };
}

describe("Login page", () => {
  beforeEach(() => {
    vi.resetAllMocks();
    cleanup();
  });

  it("submits username + password", async () => {
    const { login } = setup();
    login.mockResolvedValue(undefined);
    const { getByLabelText, getByRole } = render(
      <MemoryRouter initialEntries={["/login"]}>
        <Routes>
          <Route path="/login" element={<Login />} />
          <Route path="/" element={<div>HOME</div>} />
        </Routes>
      </MemoryRouter>
    );
    fireEvent.change(getByLabelText(/username/i), {
      target: { value: "alice" },
    });
    fireEvent.change(getByLabelText(/password/i), {
      target: { value: "hunter2hunter2" },
    });
    fireEvent.click(getByRole("button", { name: /sign in/i }));
    await waitFor(() =>
      expect(login).toHaveBeenCalledWith("alice", "hunter2hunter2")
    );
  });

  it("shows error on failed login", async () => {
    const { login } = setup();
    login.mockRejectedValue(
      Object.assign(new Error("401"), { code: "invalid_credentials" })
    );
    const { getByLabelText, getByRole, getByText } = render(
      <MemoryRouter initialEntries={["/login"]}>
        <Routes>
          <Route path="/login" element={<Login />} />
        </Routes>
      </MemoryRouter>
    );
    fireEvent.change(getByLabelText(/username/i), {
      target: { value: "alice" },
    });
    fireEvent.change(getByLabelText(/password/i), {
      target: { value: "wrong" },
    });
    fireEvent.click(getByRole("button", { name: /sign in/i }));
    await waitFor(() =>
      expect(getByText(/username or password is incorrect/i)).toBeTruthy()
    );
  });

  it("redirects to `next` query param on success", async () => {
    const { login } = setup();
    login.mockResolvedValue(undefined);
    const { getByLabelText, getByRole, getByText } = render(
      <MemoryRouter initialEntries={["/login?next=/claims"]}>
        <Routes>
          <Route path="/login" element={<Login />} />
          <Route path="/claims" element={<div>CLAIMS</div>} />
        </Routes>
      </MemoryRouter>
    );
    fireEvent.change(getByLabelText(/username/i), {
      target: { value: "alice" },
    });
    fireEvent.change(getByLabelText(/password/i), {
      target: { value: "hunter2hunter2" },
    });
    fireEvent.click(getByRole("button", { name: /sign in/i }));
    await waitFor(() => expect(getByText("CLAIMS")).toBeTruthy());
  });
});
