/* eslint-disable import-x/order */
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { ApiError } from '../api-client';
import { useAuthStore } from '../auth-store';

// Mock auth-api module
vi.mock('../auth-api', () => ({
  authApi: {
    login: vi.fn(),
    register: vi.fn(),
    logout: vi.fn(),
    refresh: vi.fn(),
    exchangeToken: vi.fn(),
    getProfile: vi.fn(),
  },
}));

// Import mocked module
import { authApi } from '../auth-api';
const mockedAuthApi = vi.mocked(authApi);

const mockUser = {
  id: '1',
  email: 'test@example.com',
  phone: '0912345678',
  fullName: 'Nguyen Van A',
  avatarUrl: null,
  role: 'user',
  kycStatus: 'pending',
  isActive: true,
  createdAt: '2024-01-01',
};

describe('useAuthStore', () => {
  beforeEach(() => {
    // Reset store state
    useAuthStore.setState({
      user: null,
      isAuthenticated: false,
      isLoading: false,
      error: null,
    });
    vi.clearAllMocks();
  });

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

  describe('initial state', () => {
    it('starts with null user and unauthenticated', () => {
      const state = useAuthStore.getState();
      expect(state.user).toBeNull();
      expect(state.isAuthenticated).toBe(false);
      expect(state.isLoading).toBe(false);
      expect(state.error).toBeNull();
    });
  });

  describe('login', () => {
    it('sets isAuthenticated and fetches profile on success', async () => {
      mockedAuthApi.login.mockResolvedValue({ message: 'ok' });
      mockedAuthApi.getProfile.mockResolvedValue(mockUser);

      await useAuthStore.getState().login({ phone: '0912345678', password: 'pass123' });

      const state = useAuthStore.getState();
      expect(state.isAuthenticated).toBe(true);
      expect(state.user).toEqual(mockUser);
      expect(state.isLoading).toBe(false);
      expect(state.error).toBeNull();
    });

    it('sets error on login failure', async () => {
      mockedAuthApi.login.mockRejectedValue(new ApiError(401, 'Sai mật khẩu'));

      await expect(
        useAuthStore.getState().login({ phone: '0912345678', password: 'wrong' }),
      ).rejects.toThrow();

      const state = useAuthStore.getState();
      expect(state.isAuthenticated).toBe(false);
      expect(state.error).toBe('Sai mật khẩu');
      expect(state.isLoading).toBe(false);
    });

    it('uses default error message for non-ApiError', async () => {
      mockedAuthApi.login.mockRejectedValue(new Error('Network error'));

      await expect(
        useAuthStore.getState().login({ phone: '0912345678', password: 'pass' }),
      ).rejects.toThrow();

      expect(useAuthStore.getState().error).toBe('Đăng nhập thất bại');
    });
  });

  describe('register', () => {
    it('sets isAuthenticated and fetches profile on success', async () => {
      mockedAuthApi.register.mockResolvedValue({ message: 'ok' });
      mockedAuthApi.getProfile.mockResolvedValue(mockUser);

      await useAuthStore.getState().register({
        phone: '0912345678',
        password: 'password123',
        fullName: 'Nguyen Van A',
      });

      const state = useAuthStore.getState();
      expect(state.isAuthenticated).toBe(true);
      expect(state.user).toEqual(mockUser);
    });

    it('sets error on register failure', async () => {
      mockedAuthApi.register.mockRejectedValue(new ApiError(409, 'Số điện thoại đã tồn tại'));

      await expect(
        useAuthStore.getState().register({
          phone: '0912345678',
          password: 'pass',
          fullName: 'Test',
        }),
      ).rejects.toThrow();

      expect(useAuthStore.getState().error).toBe('Số điện thoại đã tồn tại');
    });
  });

  describe('logout', () => {
    it('clears user and auth state', async () => {
      useAuthStore.setState({ user: mockUser, isAuthenticated: true });
      mockedAuthApi.logout.mockResolvedValue({ message: 'ok' });

      await useAuthStore.getState().logout();

      const state = useAuthStore.getState();
      expect(state.user).toBeNull();
      expect(state.isAuthenticated).toBe(false);
    });

    it('clears state even if API logout fails', async () => {
      useAuthStore.setState({ user: mockUser, isAuthenticated: true });
      mockedAuthApi.logout.mockRejectedValue(new Error('Network error'));

      await useAuthStore.getState().logout();

      expect(useAuthStore.getState().user).toBeNull();
      expect(useAuthStore.getState().isAuthenticated).toBe(false);
    });
  });

  describe('refreshToken', () => {
    it('returns true and sets authenticated on success', async () => {
      mockedAuthApi.refresh.mockResolvedValue({ message: 'ok' });

      const result = await useAuthStore.getState().refreshToken();

      expect(result).toBe(true);
      expect(useAuthStore.getState().isAuthenticated).toBe(true);
    });

    it('returns false and clears state on failure', async () => {
      useAuthStore.setState({ user: mockUser, isAuthenticated: true });
      mockedAuthApi.refresh.mockRejectedValue(new Error('expired'));

      const result = await useAuthStore.getState().refreshToken();

      expect(result).toBe(false);
      expect(useAuthStore.getState().user).toBeNull();
      expect(useAuthStore.getState().isAuthenticated).toBe(false);
    });
  });

  describe('fetchProfile', () => {
    it('fetches and sets user profile', async () => {
      mockedAuthApi.getProfile.mockResolvedValue(mockUser);

      await useAuthStore.getState().fetchProfile();

      expect(useAuthStore.getState().user).toEqual(mockUser);
      expect(useAuthStore.getState().isAuthenticated).toBe(true);
    });

    it('attempts refresh on 401 and retries profile', async () => {
      mockedAuthApi.getProfile
        .mockRejectedValueOnce(new ApiError(401, 'Unauthorized'))
        .mockResolvedValueOnce(mockUser);
      mockedAuthApi.refresh.mockResolvedValue({ message: 'ok' });

      await useAuthStore.getState().fetchProfile();

      expect(mockedAuthApi.refresh).toHaveBeenCalled();
      expect(useAuthStore.getState().user).toEqual(mockUser);
    });
  });

  describe('handleOAuthCallback', () => {
    it('exchanges token and fetches profile', async () => {
      mockedAuthApi.exchangeToken.mockResolvedValue({ message: 'ok' });
      mockedAuthApi.getProfile.mockResolvedValue(mockUser);

      await useAuthStore.getState().handleOAuthCallback('access', 'refresh', 3600);

      expect(mockedAuthApi.exchangeToken).toHaveBeenCalledWith('access', 'refresh', 3600);
      expect(useAuthStore.getState().isAuthenticated).toBe(true);
      expect(useAuthStore.getState().user).toEqual(mockUser);
    });
  });

  describe('clearError', () => {
    it('clears the error state', () => {
      useAuthStore.setState({ error: 'Some error' });
      useAuthStore.getState().clearError();
      expect(useAuthStore.getState().error).toBeNull();
    });
  });
});
