import { describe, it, expect, vi, beforeEach } from 'vitest'
import { setActivePinia, createPinia } from 'pinia'
import { useAuthStore, getDefaultRouteForRole } from '@/stores/auth'

vi.mock('@/api/client', () => ({
  post: vi.fn(),
  get: vi.fn(),
}))

vi.mock('@/router', () => ({
  default: { push: vi.fn() },
}))

import { post, get } from '@/api/client'
import router from '@/router'

const mockedPost = vi.mocked(post)
const mockedGet = vi.mocked(get)
const mockedRouterPush = vi.mocked(router.push)

beforeEach(() => {
  setActivePinia(createPinia())
  vi.clearAllMocks()
  localStorage.clear()
})

describe('getDefaultRouteForRole', () => {
  it.each([
    ['INTAKE_CLERK', '/intake'],
    ['DATA_ENTRY_CLERK', '/entry'],
    ['VERIFIER', '/verification'],
    ['CLINICAL_APPROVER', '/approval'],
    ['CLINICIAN', '/live-capture'],
    ['ADMINISTRATOR', '/dashboard'],
    ['UNKNOWN_ROLE', '/login'],
    ['', '/login'],
  ])('returns %s for role %s', (role, expected) => {
    expect(getDefaultRouteForRole(role)).toBe(expected)
  })
})

describe('useAuthStore', () => {
  describe('initial state', () => {
    it('starts unauthenticated when localStorage is empty', () => {
      const store = useAuthStore()
      expect(store.isAuthenticated).toBe(false)
      expect(store.user).toBeNull()
      expect(store.userRole).toBe('')
      expect(store.userId).toBe('')
      expect(store.userFullName).toBe('')
    })

    it('hydrates from localStorage on creation', () => {
      localStorage.setItem('vigilcare_token', 'stored-token')
      localStorage.setItem('vigilcare_refresh_token', 'stored-refresh')
      localStorage.setItem(
        'vigilcare_user',
        JSON.stringify({ id: 'u1', username: 'clerk1', fullName: 'Clerk One', role: 'DATA_ENTRY_CLERK' }),
      )

      setActivePinia(createPinia())
      const store = useAuthStore()

      expect(store.isAuthenticated).toBe(true)
      expect(store.userRole).toBe('DATA_ENTRY_CLERK')
      expect(store.userId).toBe('u1')
      expect(store.userFullName).toBe('Clerk One')
    })
  })

  describe('role-based permissions', () => {
    function storeWithRole(role: string) {
      const store = useAuthStore()
      store.user = { id: 'u1', username: 'test', fullName: 'Test', role }
      store.token = 'tok'
      return store
    }

    it('INTAKE_CLERK can intake only', () => {
      const store = storeWithRole('INTAKE_CLERK')
      expect(store.canIntake).toBe(true)
      expect(store.canEntry).toBe(false)
      expect(store.canVerify).toBe(false)
      expect(store.canApprove).toBe(false)
      expect(store.canLiveCapture).toBe(false)
      expect(store.canSupervise).toBe(false)
    })

    it('DATA_ENTRY_CLERK can entry only', () => {
      const store = storeWithRole('DATA_ENTRY_CLERK')
      expect(store.canEntry).toBe(true)
      expect(store.canIntake).toBe(false)
      expect(store.canVerify).toBe(false)
      expect(store.canApprove).toBe(false)
    })

    it('VERIFIER can verify only', () => {
      const store = storeWithRole('VERIFIER')
      expect(store.canVerify).toBe(true)
      expect(store.canIntake).toBe(false)
      expect(store.canEntry).toBe(false)
      expect(store.canApprove).toBe(false)
    })

    it('CLINICAL_APPROVER can verify and approve', () => {
      const store = storeWithRole('CLINICAL_APPROVER')
      expect(store.canVerify).toBe(true)
      expect(store.canApprove).toBe(true)
      expect(store.canIntake).toBe(false)
      expect(store.canEntry).toBe(false)
    })

    it('CLINICIAN can live capture only', () => {
      const store = storeWithRole('CLINICIAN')
      expect(store.canLiveCapture).toBe(true)
      expect(store.canIntake).toBe(false)
      expect(store.canEntry).toBe(false)
      expect(store.canVerify).toBe(false)
      expect(store.canSupervise).toBe(false)
    })

    it('ADMINISTRATOR has all permissions', () => {
      const store = storeWithRole('ADMINISTRATOR')
      expect(store.canIntake).toBe(true)
      expect(store.canEntry).toBe(true)
      expect(store.canVerify).toBe(true)
      expect(store.canApprove).toBe(true)
      expect(store.canLiveCapture).toBe(true)
      expect(store.canSupervise).toBe(true)
    })
  })

  describe('login', () => {
    it('sets token, user, and localStorage on successful login', async () => {
      mockedPost.mockResolvedValueOnce({
        success: true,
        statusCode: 200,
        data: {
          token: 'jwt-tok',
          refreshToken: 'ref-tok',
          userId: 'u-123',
          username: 'entry1',
          fullName: 'Entry Clerk',
          role: 'DATA_ENTRY_CLERK',
        },
        error: null,
      })

      const store = useAuthStore()
      await store.login({ username: 'entry1', password: 'password' })

      expect(store.isAuthenticated).toBe(true)
      expect(store.token).toBe('jwt-tok')
      expect(store.refreshToken).toBe('ref-tok')
      expect(store.user).toEqual({
        id: 'u-123',
        username: 'entry1',
        fullName: 'Entry Clerk',
        role: 'DATA_ENTRY_CLERK',
      })
      expect(localStorage.setItem).toHaveBeenCalledWith('vigilcare_token', 'jwt-tok')
      expect(localStorage.setItem).toHaveBeenCalledWith('vigilcare_refresh_token', 'ref-tok')
      expect(mockedRouterPush).toHaveBeenCalledWith('/entry')
    })

    it('navigates to role-specific default route', async () => {
      mockedPost.mockResolvedValueOnce({
        success: true,
        statusCode: 200,
        data: {
          token: 't',
          refreshToken: 'r',
          userId: 'u1',
          username: 'admin',
          fullName: 'Admin',
          role: 'ADMINISTRATOR',
        },
        error: null,
      })

      const store = useAuthStore()
      await store.login({ username: 'admin', password: 'password' })

      expect(mockedRouterPush).toHaveBeenCalledWith('/dashboard')
    })

    it('throws on failed login', async () => {
      mockedPost.mockResolvedValueOnce({
        success: false,
        statusCode: 401,
        data: null,
        error: { message: 'Invalid credentials', code: 'AUTH_FAILED' },
      })

      const store = useAuthStore()
      await expect(store.login({ username: 'bad', password: 'wrong' })).rejects.toThrow(
        'Invalid credentials',
      )
      expect(store.isAuthenticated).toBe(false)
    })

    it('throws generic message when error has no message', async () => {
      mockedPost.mockResolvedValueOnce({
        success: false,
        statusCode: 500,
        data: null,
        error: null,
      })

      const store = useAuthStore()
      await expect(store.login({ username: 'x', password: 'y' })).rejects.toThrow('Login failed')
    })
  })

  describe('fetchCurrentUser', () => {
    it('updates user from /auth/me response', async () => {
      mockedGet.mockResolvedValueOnce({
        success: true,
        statusCode: 200,
        data: { id: 'u-99', username: 'verifier1', fullName: 'Verifier One', role: 'VERIFIER' },
        error: null,
      })

      const store = useAuthStore()
      await store.fetchCurrentUser()

      expect(store.user).toEqual({
        id: 'u-99',
        username: 'verifier1',
        fullName: 'Verifier One',
        role: 'VERIFIER',
      })
      expect(localStorage.setItem).toHaveBeenCalledWith(
        'vigilcare_user',
        JSON.stringify(store.user),
      )
    })

    it('does not update user on failed response', async () => {
      mockedGet.mockResolvedValueOnce({
        success: false,
        statusCode: 401,
        data: null,
        error: null,
      })

      const store = useAuthStore()
      store.user = { id: 'old', username: 'old', fullName: 'Old', role: 'VERIFIER' }
      await store.fetchCurrentUser()

      expect(store.user?.id).toBe('old')
    })
  })

  describe('logout', () => {
    it('clears state, localStorage, and navigates to /login', async () => {
      mockedPost.mockResolvedValueOnce({ success: true, statusCode: 200, data: null, error: null })

      const store = useAuthStore()
      store.token = 'tok'
      store.refreshToken = 'ref'
      store.user = { id: 'u1', username: 'x', fullName: 'X', role: 'VERIFIER' }

      await store.logout()

      expect(store.token).toBeNull()
      expect(store.refreshToken).toBeNull()
      expect(store.user).toBeNull()
      expect(store.isAuthenticated).toBe(false)
      expect(localStorage.removeItem).toHaveBeenCalledWith('vigilcare_token')
      expect(localStorage.removeItem).toHaveBeenCalledWith('vigilcare_refresh_token')
      expect(localStorage.removeItem).toHaveBeenCalledWith('vigilcare_user')
      expect(mockedRouterPush).toHaveBeenCalledWith('/login')
    })

    it('posts refresh token to server on logout', async () => {
      mockedPost.mockResolvedValueOnce({ success: true, statusCode: 200, data: null, error: null })

      const store = useAuthStore()
      store.refreshToken = 'my-refresh-tok'

      await store.logout()

      expect(mockedPost).toHaveBeenCalledWith('auth/logout', { refreshToken: 'my-refresh-tok' })
    })

    it('still clears local session if server revoke fails', async () => {
      mockedPost.mockRejectedValueOnce(new Error('Network error'))

      const store = useAuthStore()
      store.token = 'tok'
      store.refreshToken = 'ref'

      await store.logout()

      expect(store.token).toBeNull()
      expect(store.isAuthenticated).toBe(false)
      expect(mockedRouterPush).toHaveBeenCalledWith('/login')
    })

    it('skips server call when no refresh token', async () => {
      const store = useAuthStore()
      store.token = 'tok'
      store.refreshToken = null

      await store.logout()

      expect(mockedPost).not.toHaveBeenCalled()
      expect(store.token).toBeNull()
    })
  })
})
