import { act } from "react";
import { createRoot, type Root } from "react-dom/client";
import { MemoryRouter, Route, Routes } from "react-router-dom";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import AuthCallbackPage from "./AuthCallbackPage";
import AuthGuard from "./AuthGuard";
import AuthPage from "./AuthPage";
import LoginPage from "./LoginPage";

const authState = {
  isAuthenticated: false,
  isLoading: false,
  activeNavigator: undefined as string | undefined,
  error: null as Error | null,
  user: undefined as
    | {
        state?: unknown;
      }
    | undefined,
  signinRedirect: vi.fn(),
};

vi.mock("react-oidc-context", () => ({
  useAuth: () => authState,
}));

vi.mock("../../lib/auth", () => ({
  userManager: {
    getUser: vi.fn(async () => undefined),
    signinPopupCallback: vi.fn(async () => undefined),
  },
}));

const roots: Root[] = [];

beforeEach(() => {
  authState.isAuthenticated = false;
  authState.isLoading = false;
  authState.activeNavigator = undefined;
  authState.error = null;
  authState.user = undefined;
  authState.signinRedirect.mockReset();
  authState.signinRedirect.mockResolvedValue(undefined);
});

afterEach(() => {
  for (const root of roots.splice(0)) {
    act(() => {
      root.unmount();
    });
  }
});

async function renderWithRouter(
  element: React.ReactElement,
  {
    entry = "/",
    path = "*",
  }: {
    entry?: string;
    path?: string;
  } = {},
) {
  const container = document.createElement("div");
  document.body.appendChild(container);
  const root = createRoot(container);
  roots.push(root);

  await act(async () => {
    root.render(
      <MemoryRouter initialEntries={[entry]}>
        <Routes>
          <Route path={path} element={element}>
            <Route index element={<div>Protected outlet</div>} />
          </Route>
          <Route path="/login" element={<div>Login route</div>} />
          <Route path="/clients" element={<div>Clients route</div>} />
          <Route path="/profile" element={<div>Profile route</div>} />
        </Routes>
      </MemoryRouter>,
    );
  });

  await act(async () => {
    await new Promise((resolve) => setTimeout(resolve, 0));
  });

  return container;
}

describe("devfront auth pages", () => {
  it("renders the static auth planning page", async () => {
    const container = await renderWithRouter(<AuthPage />);

    expect(container.textContent).toContain("Admin auth guardrails");
    expect(container.textContent).toContain("Device approval");
  });

  it("renders login page and starts SSO redirect from the action button", async () => {
    const container = await renderWithRouter(<LoginPage />, {
      entry: "/login?returnTo=/profile",
      path: "/login",
    });

    expect(container.textContent).toContain("개발자 포털 로그인");

    const loginButton = Array.from(container.querySelectorAll("button")).find(
      (button) => button.textContent?.includes("SSO 계정으로 로그인"),
    );
    await act(async () => {
      (loginButton as HTMLButtonElement).click();
    });

    expect(authState.signinRedirect).toHaveBeenCalledWith({
      state: { returnTo: "/clients" },
    });
  });

  it("shows AuthGuard loading, error, redirect, and protected outlet states", async () => {
    authState.isLoading = true;
    const loading = await renderWithRouter(<AuthGuard />);
    expect(loading.textContent).toContain("Loading...");

    authState.isLoading = false;
    authState.error = new Error("OIDC failed");
    const error = await renderWithRouter(<AuthGuard />);
    expect(error.textContent).toContain("Authentication Error");

    const retryButton = error.querySelector("button") as HTMLButtonElement;
    await act(async () => {
      retryButton.click();
    });
    expect(authState.signinRedirect).toHaveBeenCalled();

    authState.error = null;
    const redirected = await renderWithRouter(<AuthGuard />);
    expect(redirected.textContent).toContain("Login route");

    authState.isAuthenticated = true;
    const protectedPage = await renderWithRouter(<AuthGuard />);
    expect(protectedPage.textContent).toContain("Protected outlet");
  });

  it("navigates from callback by auth result and stored return target", async () => {
    authState.isAuthenticated = true;
    authState.user = { state: { returnTo: "/profile" } };

    const authenticated = await renderWithRouter(<AuthCallbackPage />, {
      entry: "/auth/callback",
      path: "/auth/callback",
    });
    expect(authenticated.textContent).toContain("Profile route");

    authState.isAuthenticated = false;
    authState.error = new Error("callback failed");
    const failed = await renderWithRouter(<AuthCallbackPage />, {
      entry: "/auth/callback",
      path: "/auth/callback",
    });
    expect(failed.textContent).toContain("Login route");
  });
});
