import { describe, it, expect, vi, beforeEach } from 'vitest'
import { setActivePinia, createPinia } from 'pinia'
import { useUserStore } from '@/store/user'
import * as loginApi from '@/api/login'
import * as auth from '@/utils/auth'
import { mockLoginResponse, mockAdminInfo, wrapResponse, wrapErrorResponse } from '../../fixtures'

// Mock modules
vi.mock('@/api/login', () => ({
  login: vi.fn(),
  getInfo: vi.fn(),
  logout: vi.fn()
}))

vi.mock('@/utils/auth', () => ({
  getToken: vi.fn(() => ''),
  setToken: vi.fn(),
  removeToken: vi.fn()
}))

describe('User Store', () => {
  beforeEach(() => {
    setActivePinia(createPinia())
    vi.clearAllMocks()
  })

  describe('loginAction', () => {
    it('should login successfully and store token', async () => {
      const mockResponse = wrapResponse(mockLoginResponse)
      vi.mocked(loginApi.login).mockResolvedValue(mockResponse)

      const store = useUserStore()
      const result = await store.loginAction({ username: 'admin', password: 'password' })

      expect(result.success).toBe(true)
      expect(store.token).toBe(mockLoginResponse.token)
      expect(store.userInfo?.username).toBe(mockLoginResponse.admin.username)
      // setToken now takes (token, expiresIn)
      expect(auth.setToken).toHaveBeenCalledWith(mockLoginResponse.token, mockLoginResponse.expiresIn)
    })

    it('should not store token on login failure', async () => {
      const mockResponse = wrapErrorResponse('用户名或密码错误') as any
      vi.mocked(loginApi.login).mockResolvedValue(mockResponse)

      const store = useUserStore()
      const result = await store.loginAction({ username: 'wrong', password: 'wrong' })

      expect(result.success).toBe(false)
      expect(store.token).toBe('')
      expect(auth.setToken).not.toHaveBeenCalled()
    })
  })

  describe('getUserInfo', () => {
    it('should fetch and store user info', async () => {
      const mockResponse = wrapResponse(mockAdminInfo)
      vi.mocked(loginApi.getInfo).mockResolvedValue(mockResponse)

      const store = useUserStore()
      const result = await store.getUserInfo()

      expect(result.success).toBe(true)
      expect(store.userInfo?.username).toBe(mockAdminInfo.username)
      expect(store.userInfo?.role).toBe(mockAdminInfo.role)
    })
  })

  describe('logoutAction', () => {
    it('should clear token and user info on logout', async () => {
      vi.mocked(loginApi.logout).mockResolvedValue(wrapResponse(null))

      const store = useUserStore()
      // Set initial state
      store.token = mockLoginResponse.token
      store.userInfo = mockAdminInfo

      await store.logoutAction()

      expect(store.token).toBe('')
      expect(store.userInfo).toBeNull()
      expect(auth.removeToken).toHaveBeenCalled()
    })

    it('should clear state in finally block even if logout API fails', async () => {
      vi.mocked(loginApi.logout).mockRejectedValue(new Error('Network error'))

      const store = useUserStore()
      store.token = mockLoginResponse.token
      store.userInfo = mockAdminInfo

      // logoutAction now catches errors internally and still clears state
      await store.logoutAction()

      // State should be cleared in finally block
      expect(store.token).toBe('')
      expect(store.userInfo).toBeNull()
      expect(auth.removeToken).toHaveBeenCalled()
    })
  })

  describe('resetToken', () => {
    it('should clear all auth state', () => {
      const store = useUserStore()
      store.token = mockLoginResponse.token
      store.userInfo = mockAdminInfo

      store.resetToken()

      expect(store.token).toBe('')
      expect(store.userInfo).toBeNull()
      expect(auth.removeToken).toHaveBeenCalled()
    })
  })
})
