import React from 'react';
import { render, screen, waitFor } from '@testing-library/react';
import { MemoryRouter, Route, Routes } from 'react-router-dom';
import AuthCallback from './AuthCallback';
import { AuthContext } from '../../contexts/AuthContext';
import { AccountContext } from '../../contexts/AccountContext';

jest.mock('../../components/ParticleBackground/ParticleBackground', () => () => null);

jest.mock('../../utils/revenueCat', () => ({
  logInRevenueCat: () => Promise.resolve(),
  purchasePlan: () => Promise.resolve(),
}));

const mockPost = jest.fn();
const mockGet = jest.fn();
const mockApplyAccessToken = jest.fn();
const assignMock = jest.fn();

jest.mock('../../../axiosApi', () => ({
  axiosInstance: {
    post: (...args: unknown[]) => mockPost(...args),
    get: (...args: unknown[]) => mockGet(...args),
    defaults: { headers: { common: {} as Record<string, string | null> } },
  },
  applyAccessToken: (...args: unknown[]) => mockApplyAccessToken(...args),
}));

const renderCallback = (query: string) => {
  const setAuthentication = jest.fn();
  const setNeedsNewPassword = jest.fn();
  const setAccount = jest.fn();

  render(
    <MemoryRouter initialEntries={[`/auth/callback/${query}`]}>
      <AuthContext.Provider
        value={{
          authenticated: false,
          setAuthentication,
          needsNewPassword: false,
          setNeedsNewPassword,
          loading: false,
        }}
      >
        <AccountContext.Provider value={{ account: undefined, setAccount }}>
          <Routes>
            <Route path="/auth/callback/" element={<AuthCallback />} />
            <Route path="/" element={<div>Home</div>} />
            <Route path="/terms_of_service/" element={<div>TOS</div>} />
            <Route path="/signin/" element={<div>Sign In Page</div>} />
            <Route path="/document_storage/" element={<div>Document Storage Page</div>} />
          </Routes>
        </AccountContext.Provider>
      </AuthContext.Provider>
    </MemoryRouter>
  );

  return { setAuthentication, setNeedsNewPassword, setAccount };
};

describe('AuthCallback', () => {
  const originalLocation = window.location;

  beforeEach(() => {
    localStorage.clear();
    mockPost.mockReset();
    mockGet.mockReset();
    mockApplyAccessToken.mockReset();
    assignMock.mockReset();
    Object.defineProperty(window, 'location', {
      configurable: true,
      value: { ...originalLocation, assign: assignMock, origin: 'http://localhost' },
    });
  });

  afterEach(() => {
    Object.defineProperty(window, 'location', {
      configurable: true,
      value: originalLocation,
    });
  });

  it('shows oauth error from query string', async () => {
    renderCallback('?error=email_unverified');

    expect(await screen.findByText(/email with this provider is not verified/i)).toBeInTheDocument();
    expect(screen.getByRole('link', { name: /Back to Sign In/i })).toBeInTheDocument();
  });

  it('stores tokens and navigates home when TOS signed', async () => {
    mockGet.mockResolvedValue({
      data: {
        email: 'user@example.com',
        first_name: 'Jane',
        last_name: 'Doe',
        is_company_manager: true,
        has_signed_tos: true,
        has_usable_password: false,
        company: { id: 1, name: 'Acme', state: 'CA', zipcode: '90001', address: '1 Main' },
      },
    });

    const { setAuthentication } = renderCallback(
      '?access=access-token&refresh=refresh-token&created=0&needs_checkout=0'
    );

    await waitFor(() => {
      expect(localStorage.getItem('access_token')).toBe('access-token');
      expect(localStorage.getItem('refresh_token')).toBe('refresh-token');
      expect(mockApplyAccessToken).toHaveBeenCalledWith('access-token');
      expect(setAuthentication).toHaveBeenCalledWith(true);
      expect(screen.getByText('Home')).toBeInTheDocument();
    });
  });

  it('starts Stripe checkout for new SSO accounts', async () => {
    mockGet.mockResolvedValue({
      data: {
        email: 'new@example.com',
        first_name: '',
        last_name: '',
        is_company_manager: true,
        has_signed_tos: false,
        has_usable_password: false,
        company: { id: 2, name: "new@example.com's workspace", state: 'NA', zipcode: '00000', address: 'N/A' },
      },
    });
    mockPost.mockResolvedValue({ data: { checkout_url: 'https://checkout.stripe.test/session' } });

    renderCallback(
      '?access=access-token&refresh=refresh-token&created=1&needs_checkout=1'
    );

    await waitFor(() => {
      expect(mockPost).toHaveBeenCalledWith(
        '/monetization/checkout/',
        expect.objectContaining({
          success_url: expect.stringContaining('/billing/success'),
          cancel_url: expect.stringContaining('/billing/cancel'),
        })
      );
      expect(assignMock).toHaveBeenCalledWith('https://checkout.stripe.test/session');
    });
  });

  it('redirects drive-link callbacks straight to Documents (#83/#84)', async () => {
    renderCallback('?drive_connected=1');

    await waitFor(() => {
      expect(screen.getByText('Document Storage Page')).toBeInTheDocument();
    });
    expect(mockGet).not.toHaveBeenCalled();
  });
});
