import { describe, it, expect, vi, beforeEach } from 'vitest'
import { setActivePinia, createPinia } from 'pinia'
import { useUserStore } from '../user'

// Mock the api module
vi.mock('@/utils/request', () => ({
  api: {
    get: vi.fn(),
    post: vi.fn(),
    put: vi.fn(),
  },
}))

describe('useUserStore', () => {
  beforeEach(() => {
    setActivePinia(createPinia())
    localStorage.clear()
    vi.restoreAllMocks()
  })

  describe('initial state', () => {
    it('starts with null values when localStorage is empty', () => {
      const store = useUserStore()

      expect(store.token).toBeNull()
      expect(store.refreshToken).toBeNull()
      expect(store.user).toBeNull()
      expect(store.isLoggedIn).toBe(false)
      expect(store.username).toBe('')
    })

    it('restores token from localStorage', () => {
      localStorage.setItem('access_token', 'header.payload.signature')
      localStorage.setItem('refresh_token', 'rh.rp.rs')

      const store = useUserStore()

      expect(store.token).toBe('header.payload.signature')
      expect(store.refreshToken).toBe('rh.rp.rs')
      expect(store.isLoggedIn).toBe(true)
    })

    it('restores user info from localStorage', () => {
      const savedUser = { id: 1, username: 'admin', email: 'a@b.com', role: 'administrator', status: 1 }
      localStorage.setItem('user', JSON.stringify(savedUser))

      const store = useUserStore()

      expect(store.user).toEqual(savedUser)
      expect(store.username).toBe('admin')
      expect(store.isAdmin).toBe(true)
    })

    it('handles corrupted user data in localStorage', () => {
      localStorage.setItem('user', 'invalid-json{')

      const store = useUserStore()

      expect(store.user).toBeNull()
    })
  })

  describe('setToken', () => {
    it('sets tokens in store and localStorage', () => {
      const store = useUserStore()

      store.setToken('header.payload.signature', 'rh.rp.rs')

      expect(store.token).toBe('header.payload.signature')
      expect(store.refreshToken).toBe('rh.rp.rs')
      expect(localStorage.getItem('access_token')).toBe('header.payload.signature')
      expect(localStorage.getItem('refresh_token')).toBe('rh.rp.rs')
      expect(store.isLoggedIn).toBe(true)
    })
  })

  describe('setToken with remember=false', () => {
    it('does not persist tokens to localStorage', () => {
      const store = useUserStore()

      store.setToken('h.p.s', 'r.p.s', false)

      expect(store.token).toBe('h.p.s')
      expect(store.refreshToken).toBe('r.p.s')
      expect(localStorage.getItem('access_token')).toBeNull()
      expect(localStorage.getItem('refresh_token')).toBeNull()
    })

    it('clears existing user from localStorage', () => {
      localStorage.setItem('user', JSON.stringify({ id: 1, username: 'old' }))

      const store = useUserStore()
      store.setToken('h.p.s', 'r.p.s', false)

      expect(localStorage.getItem('user')).toBeNull()
    })
  })

  describe('setUser', () => {
    it('persists to localStorage when remember=true', () => {
      const store = useUserStore()
      store.setToken('h.p.s', 'r.p.s', true)
      const userInfo = { id: 1, username: 'test', email: 't@t.com', role: 'user', status: 1 }

      store.setUser(userInfo)

      expect(store.user).toEqual(userInfo)
      expect(store.username).toBe('test')
      expect(store.isAdmin).toBe(false)
      expect(JSON.parse(localStorage.getItem('user')!)).toEqual(userInfo)
    })

    it('does not persist to localStorage by default (remember=false)', () => {
      const store = useUserStore()
      const userInfo = { id: 1, username: 'test', email: 't@t.com', role: 'user', status: 1 }

      store.setUser(userInfo)

      expect(store.user).toEqual(userInfo)
      expect(localStorage.getItem('user')).toBeNull()
    })

    it('does not write to localStorage after setToken(remember=false)', () => {
      const store = useUserStore()
      store.setToken('h.p.s', 'r.p.s', false)

      const userInfo = { id: 1, username: 'test', email: 't@t.com', role: 'user', status: 1 }
      store.setUser(userInfo)

      expect(store.user).toEqual(userInfo)
      expect(localStorage.getItem('user')).toBeNull()
    })
  })

  describe('logout', () => {
    it('clears all state and localStorage', () => {
      const store = useUserStore()
      store.setToken('h.p.s', 'r.p.s')
      store.setUser({ id: 1, username: 'a', email: 'a@a.com', role: 'administrator', status: 1 })

      store.logout()

      expect(store.token).toBeNull()
      expect(store.refreshToken).toBeNull()
      expect(store.user).toBeNull()
      expect(store.isLoggedIn).toBe(false)
      expect(localStorage.getItem('access_token')).toBeNull()
      expect(localStorage.getItem('refresh_token')).toBeNull()
      expect(localStorage.getItem('user')).toBeNull()
    })

    it('resets _remember so subsequent setUser does not persist', () => {
      const store = useUserStore()
      store.setToken('h.p.s', 'r.p.s', true)
      store.setUser({ id: 1, username: 'a', email: 'a@a.com', role: 'administrator', status: 1 })
      expect(localStorage.getItem('user')).not.toBeNull()

      store.logout()
      store.setUser({ id: 2, username: 'b', email: 'b@b.com', role: 'user', status: 1 })

      expect(store.user?.username).toBe('b')
      expect(localStorage.getItem('user')).toBeNull()
    })
  })

  describe('fetchCurrentUser', () => {
    it('fetches user from API and updates store', async () => {
      const { api } = await import('@/utils/request')
      const mockUser = { id: 1, username: 'fetched', email: 'f@f.com', role: 'administrator', status: 1 }
      vi.mocked(api.get).mockResolvedValueOnce(mockUser)

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

      expect(api.get).toHaveBeenCalledWith('/api/v1/me')
      expect(result).toEqual(mockUser)
      expect(store.user).toEqual(mockUser)
      expect(store.isAdmin).toBe(true)
    })
  })

  describe('updateProfile', () => {
    it('calls API and updates local state', async () => {
      const { api } = await import('@/utils/request')
      const updatedUser = { id: 1, username: 'u', email: 'new@e.com', role: 'user', status: 1 }
      vi.mocked(api.put).mockResolvedValueOnce(updatedUser)

      const store = useUserStore()
      store.setToken('h.p.s', 'r.p.s', true)
      const result = await store.updateProfile({ email: 'new@e.com' })

      expect(api.put).toHaveBeenCalledWith('/api/v1/me/profile', { email: 'new@e.com' })
      expect(result).toEqual(updatedUser)
      expect(store.user).toEqual(updatedUser)
      expect(JSON.parse(localStorage.getItem('user')!)).toEqual(updatedUser)
    })

    it('does not update state on API error', async () => {
      const { api } = await import('@/utils/request')
      vi.mocked(api.put).mockRejectedValueOnce(new Error('fail'))

      const store = useUserStore()
      const originalUser = { id: 1, username: 'u', email: 'old@e.com', role: 'user', status: 1 }
      store.setUser(originalUser)

      await expect(store.updateProfile({ email: 'new@e.com' })).rejects.toThrow('fail')
      expect(store.user).toEqual(originalUser)
    })
  })

  describe('changePassword', () => {
    it('calls API and triggers logout on success', async () => {
      const { api } = await import('@/utils/request')
      vi.mocked(api.put).mockResolvedValueOnce(undefined)

      const store = useUserStore()
      store.setToken('h.p.s', 'r.p.s', true)
      store.setUser({ id: 1, username: 'u', email: 'u@e.com', role: 'user', status: 1 })

      await store.changePassword({
        old_password: 'old123',
        new_password: 'new123',
        new_password_confirmation: 'new123',
      })

      expect(api.put).toHaveBeenCalledWith('/api/v1/me/password', {
        old_password: 'old123',
        new_password: 'new123',
        new_password_confirmation: 'new123',
      })
      expect(store.token).toBeNull()
      expect(store.user).toBeNull()
      expect(localStorage.getItem('access_token')).toBeNull()
    })

    it('does not logout on API error', async () => {
      const { api } = await import('@/utils/request')
      vi.mocked(api.put).mockRejectedValueOnce(new Error('旧密码错误'))

      const store = useUserStore()
      store.setToken('h.p.s', 'r.p.s', true)
      store.setUser({ id: 1, username: 'u', email: 'u@e.com', role: 'user', status: 1 })

      await expect(store.changePassword({
        old_password: 'wrong',
        new_password: 'new123',
        new_password_confirmation: 'new123',
      })).rejects.toThrow('旧密码错误')

      expect(store.token).toBe('h.p.s')
      expect(store.user).not.toBeNull()
    })
  })

  describe('computed properties', () => {
    it('isAdmin returns true for admin role', () => {
      const store = useUserStore()
      store.setUser({ id: 1, username: 'a', email: 'a@a.com', role: 'administrator', status: 1 })

      expect(store.isAdmin).toBe(true)
    })

    it('isAdmin returns false for non-admin role', () => {
      const store = useUserStore()
      store.setUser({ id: 1, username: 'u', email: 'u@u.com', role: 'user', status: 1 })

      expect(store.isAdmin).toBe(false)
    })
  })
})
