import { act } from 'react';
import { createRoot, type Root } from 'react-dom/client';
import { createMemoryRouter, RouterProvider } from 'react-router-dom';
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest';

const { apiMock, oauth2Mock } = vi.hoisted(() => ({
  apiMock: vi.fn(),
  oauth2Mock: vi.fn(),
}));

vi.mock('@/lib/api', () => ({
  api: apiMock,
}));

vi.mock('@/lib/auth-client', () => ({
  signIn: { oauth2: oauth2Mock },
}));

import { SsoCallbackPage } from './sso-callback';

const mountedRoots: Root[] = [];

beforeAll(() => {
  Object.defineProperty(globalThis, 'IS_REACT_ACT_ENVIRONMENT', {
    configurable: true,
    value: true,
  });
});

beforeEach(() => {
  apiMock.mockResolvedValue([
    {
      id: 'authentik',
      name: 'Authentik',
      protocols: ['oidc'],
      configured: true,
      loginMode: 'oidc',
      callbackPath: '/api/auth/oauth2/callback/authentik',
      teamSync: { enabled: false, claim: null },
      samlFallback: { configured: false, loginUrl: null },
      warnings: [],
    },
  ]);
  oauth2Mock.mockResolvedValue({ data: null, error: null });
});

afterEach(async () => {
  for (const root of mountedRoots.splice(0)) {
    await act(async () => {
      root.unmount();
    });
  }
  document.body.replaceChildren();
  apiMock.mockReset();
  oauth2Mock.mockReset();
});

afterAll(() => {
  Reflect.deleteProperty(globalThis, 'IS_REACT_ACT_ENVIRONMENT');
});

describe('SsoCallbackPage', () => {
  it('rejects a control-character callback that normalizes to an external origin', async () => {
    const router = createMemoryRouter(
      [
        {
          path: '/auth/provider/:provider',
          element: <SsoCallbackPage />,
        },
      ],
      {
        initialEntries: ['/auth/provider/authentik?callbackURL=%2F%0A%2F%2Fevil.example'],
      },
    );
    const container = document.createElement('div');
    document.body.append(container);
    const root = createRoot(container);
    mountedRoots.push(root);

    await act(async () => {
      root.render(<RouterProvider router={router} />);
      await new Promise((resolve) => setTimeout(resolve, 0));
    });

    expect(oauth2Mock).toHaveBeenCalledWith({
      providerId: 'authentik',
      callbackURL: '/chat',
    });
  });
});
