import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { screen, act, fireEvent } from '@testing-library/react';
import { AuthProvider, useAuth } from '../../src/hooks/AuthProvider';
import React from 'react';
import { renderWithProviders } from '../test-utils';

// Mock axios
vi.mock('axios', () => ({
  default: {
    defaults: {
      headers: {
        common: {},
      },
    },
  },
}));

// Mock react-query
vi.mock('@tanstack/react-query', () => ({
  useQuery: vi.fn().mockReturnValue({
    isLoading: false,
    data: null,
    error: null,
  }),
  useQueryClient: () => ({
    clear: vi.fn(),
  }),
}));

// Mock auth client
vi.mock('../../src/api/authClient', () => ({
  authClient: {
    login: {
      $post: vi.fn(),
    },
    logout: {
      $post: vi.fn(),
    },
    me: {
      $get: vi.fn(),
    },
  },
}));

// Mock localStorage
const localStorageMock = {
  getItem: vi.fn(),
  setItem: vi.fn(),
  removeItem: vi.fn(),
  clear: vi.fn(),
};

Object.defineProperty(window, 'localStorage', {
  value: localStorageMock,
});

// Test component that uses useAuth
const TestComponent = () => {
  const auth = useAuth();
  return (
    <div>
      <div data-testid="user">{auth.user ? 'authenticated' : 'unauthenticated'}</div>
      <div data-testid="isAuthenticated">{auth.isAuthenticated ? 'true' : 'false'}</div>
      <div data-testid="isLoading">{auth.isLoading ? 'true' : 'false'}</div>
      <button onClick={() => auth.login('test', 'password')}>Login</button>
      <button onClick={() => auth.logout()}>Logout</button>
    </div>
  );
};

describe('AuthProvider', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    localStorageMock.getItem.mockReturnValue(null);
  });

  afterEach(() => {
    vi.restoreAllMocks();
  });

  it('应该提供认证上下文', () => {
    renderWithProviders(
      <AuthProvider>
        <TestComponent />
      </AuthProvider>
    );

    expect(screen.getByTestId('user')).toHaveTextContent('unauthenticated');
    expect(screen.getByTestId('isAuthenticated')).toHaveTextContent('false');
  });

  it('应该在没有AuthProvider时抛出错误', () => {
    // 抑制控制台错误输出
    const consoleError = vi.spyOn(console, 'error').mockImplementation(() => {});

    expect(() => {
      renderWithProviders(<TestComponent />);
    }).toThrow('useAuth必须在AuthProvider内部使用');

    consoleError.mockRestore();
  });

  it('应该处理登录成功', async () => {
    const mockLoginResponse = {
      status: 200,
      json: vi.fn().mockResolvedValue({
        token: 'test-token',
        user: { id: 1, username: 'test', email: 'test@example.com' },
      }),
    };

    const { authClient } = await import('../../src/api/authClient');
    (authClient.login.$post as any).mockResolvedValue(mockLoginResponse);

    renderWithProviders(
      <AuthProvider>
        <TestComponent />
      </AuthProvider>
    );

    const loginButton = screen.getByText('Login');

    await act(async () => {
      fireEvent.click(loginButton);
    });

    expect(authClient.login.$post).toHaveBeenCalledWith({
      json: {
        username: 'test',
        password: 'password',
      },
    });
  });

  it('应该处理登录失败', async () => {
    const mockLoginResponse = {
      status: 401,
      json: vi.fn().mockResolvedValue({
        message: 'Invalid credentials',
      }),
    };

    const { authClient } = await import('../../src/api/authClient');
    (authClient.login.$post as any).mockResolvedValue(mockLoginResponse);

    renderWithProviders(
      <AuthProvider>
        <TestComponent />
      </AuthProvider>
    );

    const loginButton = screen.getByText('Login');

    await act(async () => {
      fireEvent.click(loginButton);
    });

    expect(authClient.login.$post).toHaveBeenCalledWith({
      json: {
        username: 'test',
        password: 'password',
      },
    });
  });

  it('应该处理登出', async () => {
    const { authClient } = await import('../../src/api/authClient');
    (authClient.logout.$post as any).mockResolvedValue({});

    localStorageMock.getItem.mockReturnValue('test-token');

    renderWithProviders(
      <AuthProvider>
        <TestComponent />
      </AuthProvider>
    );

    const logoutButton = screen.getByText('Logout');

    await act(async () => {
      fireEvent.click(logoutButton);
    });

    expect(authClient.logout.$post).toHaveBeenCalled();
    expect(localStorageMock.removeItem).toHaveBeenCalledWith('token');
  });
});

// Helper function to fire events
function fireEvent(element: HTMLElement, event: string) {
  element.dispatchEvent(new Event(event, { bubbles: true }));
}