import { act } from "react";
import { createRoot } from "react-dom/client";
import { afterEach, describe, expect, it, vi } from "vitest";

const mocks = vi.hoisted(() => ({
  authState: {
    errorMessage: null as string | null,
    isLoading: false,
    loginStatus: "notLoggedIn",
  },
  dispatch: vi.fn(),
  sessionState: {
    data: {
      provider: "facebook",
      accessToken: "facebook-token",
    },
    status: "authenticated",
  },
  signOut: vi.fn(async () => undefined),
}));

vi.mock("next-auth/react", () => ({
  signOut: mocks.signOut,
  useSession: () => mocks.sessionState,
}));

vi.mock("../auth-context", () => ({
  useAuthDispatch: () => mocks.dispatch,
  useAuthState: () => mocks.authState,
}));

import { OAuthSessionSync } from "../oauth-session-sync";

let container: HTMLDivElement | null = null;
let root: ReturnType<typeof createRoot> | null = null;

afterEach(() => {
  if (root) act(() => root?.unmount());
  container?.remove();
  container = null;
  root = null;
  mocks.authState.errorMessage = null;
  mocks.authState.isLoading = false;
  mocks.authState.loginStatus = "notLoggedIn";
  mocks.dispatch.mockReset();
  mocks.signOut.mockReset();
  mocks.signOut.mockResolvedValue(undefined);
});

describe("OAuthSessionSync", () => {
  it("keeps the NextAuth session until backend sync succeeds", async () => {
    await renderSync();

    expect(mocks.dispatch).toHaveBeenCalledWith({
      type: "AuthFacebookSyncSubmitted",
      accessToken: "facebook-token",
    });
    expect(mocks.signOut).not.toHaveBeenCalled();

    mocks.authState.isLoading = true;
    await renderSync();
    expect(mocks.dispatch).toHaveBeenCalledOnce();
    expect(mocks.signOut).not.toHaveBeenCalled();

    mocks.authState.isLoading = false;
    mocks.authState.loginStatus = "facebook";
    await renderSync();
    expect(mocks.signOut).toHaveBeenCalledOnce();
  });

  it("clears the NextAuth session after backend sync fails", async () => {
    await renderSync();

    mocks.authState.isLoading = true;
    await renderSync();
    mocks.authState.isLoading = false;
    mocks.authState.errorMessage = "Backend sync failed";
    await renderSync();

    expect(mocks.dispatch).toHaveBeenCalledOnce();
    expect(mocks.signOut).toHaveBeenCalledOnce();
  });
});

async function renderSync(): Promise<void> {
  if (!container) {
    container = document.createElement("div");
    document.body.append(container);
    root = createRoot(container);
  }

  await act(async () => {
    root?.render(<OAuthSessionSync />);
  });
}
