import React from 'react';
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { MemoryRouter } from 'react-router-dom';
import SignIn from './SignIn';
import { AuthContext } from '../../contexts/AuthContext';
import { AccountContext } from '../../contexts/AccountContext';

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

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

const mockPost = jest.fn();
const mockGet = 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: jest.fn(),
}));

const renderSignIn = () => {
  const setAuthentication = jest.fn();
  const setNeedsNewPassword = jest.fn();
  const setAccount = jest.fn();

  render(
    <MemoryRouter>
      <AuthContext.Provider
        value={{
          authenticated: false,
          setAuthentication,
          needsNewPassword: false,
          setNeedsNewPassword,
          loading: false,
        }}
      >
        <AccountContext.Provider value={{ account: undefined, setAccount }}>
          <SignIn />
        </AccountContext.Provider>
      </AuthContext.Provider>
    </MemoryRouter>
  );

  return { setAuthentication, setNeedsNewPassword, setAccount };
};

describe('SignIn', () => {
  beforeEach(() => {
    localStorage.clear();
    mockPost.mockReset();
    mockGet.mockReset();
    mockGet.mockResolvedValue({ data: { enable_account_registration: false } });
  });

  it('renders sign-in form fields', async () => {
    renderSignIn();

    expect(screen.getByRole('heading', { name: 'Hesychia.ai' })).toBeInTheDocument();
    expect(screen.getByRole('heading', { name: 'Sign In' })).toBeInTheDocument();
    expect(screen.getByPlaceholderText('Email Address')).toBeInTheDocument();
    expect(screen.getByPlaceholderText('Password')).toBeInTheDocument();
    expect(screen.getByRole('button', { name: 'Sign In' })).toBeInTheDocument();
    expect(screen.getByRole('button', { name: 'Forgot Password?' })).toBeInTheDocument();
    await waitFor(() => {
      expect(screen.queryByRole('button', { name: /Don't have an account/i })).not.toBeInTheDocument();
    });
  });

  it('shows sign-up link when registration enabled', async () => {
    mockGet.mockResolvedValue({ data: { enable_account_registration: true } });
    renderSignIn();

    expect(await screen.findByRole('button', { name: /Don't have an account\? Sign up/i })).toBeInTheDocument();
  });

  it('shows SSO buttons when oauth providers configured', async () => {
    mockGet.mockResolvedValue({
      data: {
        enable_account_registration: false,
        oauth: { google: true, microsoft: true },
      },
    });
    renderSignIn();

    expect(await screen.findByRole('button', { name: 'Continue with Google' })).toBeInTheDocument();
    expect(screen.getByRole('button', { name: 'Sign in with Microsoft' })).toBeInTheDocument();
  });

  it('hides SSO buttons when oauth not configured', async () => {
    mockGet.mockResolvedValue({
      data: {
        enable_account_registration: false,
        oauth: { google: false, microsoft: false },
      },
    });
    renderSignIn();

    await waitFor(() => {
      expect(screen.queryByRole('button', { name: 'Continue with Google' })).not.toBeInTheDocument();
      expect(screen.queryByRole('button', { name: 'Sign in with Microsoft' })).not.toBeInTheDocument();
    });
  });

  it('does not submit when required fields empty', async () => {
    const user = userEvent.setup();
    renderSignIn();

    await user.click(screen.getByRole('button', { name: 'Sign In' }));

    await waitFor(() => {
      expect(mockPost).not.toHaveBeenCalled();
    });
  });

  it('authenticates on successful submit', async () => {
    const user = userEvent.setup();
    mockPost.mockResolvedValue({
      data: { access: 'access-token', refresh: 'refresh-token' },
    });
    mockGet.mockResolvedValue({
      data: {
        email: 'user@example.com',
        first_name: 'Jane',
        last_name: 'Doe',
        is_company_manager: false,
        has_signed_tos: true,
        has_usable_password: true,
        company: { id: 1, name: 'Acme', state: 'CA', zipcode: '90001', address: '1 Main' },
      },
    });

    const { setAuthentication, setAccount } = renderSignIn();

    await user.type(screen.getByPlaceholderText('Email Address'), 'user@example.com');
    await user.type(screen.getByPlaceholderText('Password'), 'secret123');
    await user.click(screen.getByRole('button', { name: 'Sign In' }));

    await waitFor(() => {
      expect(mockPost).toHaveBeenCalledWith('/token/obtain/', {
        username: 'user@example.com',
        password: 'secret123',
      });
    });
    await waitFor(() => {
      expect(setAuthentication).toHaveBeenCalledWith(true);
      expect(setAccount).toHaveBeenCalled();
      expect(localStorage.getItem('access_token')).toBe('access-token');
      expect(localStorage.getItem('refresh_token')).toBe('refresh-token');
    });
  });
});
