import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest'
import { useAuthStore } from '../../stores/authStore'

// Canonical test user fixtures
const adminUser = {
  id: 1,
  username: 'admin',
  nombre: 'Admin',
  rol: 'admin',
  permisos: [] as string[],
  mustChangePassword: false,
}

const cajeroUser = {
  id: 2,
  username: 'cajero',
  nombre: 'Cajero',
  rol: 'cajero',
  permisos: [] as string[],
  mustChangePassword: false,
}

describe('authStore', () => {
  beforeEach(() => {
    // Reset store state before each test
    useAuthStore.setState({
      user: null,
      accessToken: null,
      refreshToken: null,
      expiresAt: null,
    })
    localStorage.clear()
  })

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

  describe('initial state', () => {
    it('starts with null user and null accessToken', () => {
      const state = useAuthStore.getState()
      expect(state.user).toBeNull()
      expect(state.accessToken).toBeNull()
    })
  })

  describe('setAuth', () => {
    it('stores user and accessToken in state', () => {
      const payload = {
        user: adminUser,
        accessToken: 'eyJhbGciOiJSUzI1NiJ9.test.signature',
        refreshToken: 'opaque-refresh-token',
        expiresIn: 3600,
      }

      useAuthStore.getState().setAuth(payload)

      const state = useAuthStore.getState()
      expect(state.user).toEqual(payload.user)
      expect(state.accessToken).toBe(payload.accessToken)
    })

    it('persists auth data to localStorage under auth-storage key', () => {
      const payload = {
        user: adminUser,
        accessToken: 'eyJhbGciOiJSUzI1NiJ9.test.signature',
        refreshToken: 'opaque-refresh-token',
        expiresIn: 3600,
      }

      useAuthStore.getState().setAuth(payload)

      const stored = localStorage.getItem('auth-storage')
      expect(stored).not.toBeNull()
      const parsed = JSON.parse(stored!)
      expect(parsed.state.accessToken).toBe(payload.accessToken)
      expect(parsed.state.user.username).toBe('admin')
    })

    it('setAuth_persistsRefreshTokenAndExpiresAt', () => {
      const before = Date.now()
      const payload = {
        user: adminUser,
        accessToken: 'access-token-abc',
        refreshToken: 'opaque-refresh-xyz',
        expiresIn: 3600,
      }

      useAuthStore.getState().setAuth(payload)
      const after = Date.now()

      const state = useAuthStore.getState()
      expect(state.refreshToken).toBe('opaque-refresh-xyz')
      expect(state.expiresAt).not.toBeNull()
      // expiresAt should be ~Date.now() + 3600*1000
      expect(state.expiresAt!).toBeGreaterThanOrEqual(before + 3600 * 1000)
      expect(state.expiresAt!).toBeLessThanOrEqual(after + 3600 * 1000)

      // Should also be persisted in localStorage
      const stored = localStorage.getItem('auth-storage')
      const parsed = JSON.parse(stored!)
      expect(parsed.state.refreshToken).toBe('opaque-refresh-xyz')
      expect(parsed.state.expiresAt).toBeGreaterThan(0)
    })

    it('F-04-01: setAuth con permisos → user.permisos contiene los valores', () => {
      const payload = {
        user: {
          id: 1,
          username: 'admin',
          nombre: 'Admin',
          rol: 'admin',
          permisos: ['administracion:usuarios:gestionar', 'administracion:roles:gestionar'],
          mustChangePassword: false,
        },
        accessToken: 'access-token',
        refreshToken: 'refresh-token',
        expiresIn: 3600,
      }

      useAuthStore.getState().setAuth(payload)

      const state = useAuthStore.getState()
      expect(state.user?.permisos).toContain('administracion:usuarios:gestionar')
      expect(state.user?.permisos).toContain('administracion:roles:gestionar')
      expect(state.user?.permisos).toHaveLength(2)
    })

    it('F-04-02: setAuth con permisos vacíos → user.permisos es [] (no null)', () => {
      const payload = {
        user: cajeroUser,
        accessToken: 'access-token',
        refreshToken: 'refresh-token',
        expiresIn: 3600,
      }

      useAuthStore.getState().setAuth(payload)

      const state = useAuthStore.getState()
      expect(state.user?.permisos).toEqual([])
      expect(state.user?.permisos).not.toBeNull()
    })

    it('persists mustChangePassword=true in state and localStorage', () => {
      const payload = {
        user: { ...adminUser, mustChangePassword: true },
        accessToken: 'access-token',
        refreshToken: 'refresh-token',
        expiresIn: 3600,
      }

      useAuthStore.getState().setAuth(payload)

      const state = useAuthStore.getState()
      expect(state.user?.mustChangePassword).toBe(true)

      const stored = localStorage.getItem('auth-storage')
      const parsed = JSON.parse(stored!)
      expect(parsed.state.user.mustChangePassword).toBe(true)
    })

    it('persists mustChangePassword=false in state', () => {
      const payload = {
        user: { ...adminUser, mustChangePassword: false },
        accessToken: 'access-token',
        refreshToken: 'refresh-token',
        expiresIn: 3600,
      }

      useAuthStore.getState().setAuth(payload)

      const state = useAuthStore.getState()
      expect(state.user?.mustChangePassword).toBe(false)
    })
  })

  describe('updateUser', () => {
    it('updateUser_patches_mustChangePassword_preserves_rest', () => {
      useAuthStore.getState().setAuth({
        user: { ...adminUser, mustChangePassword: true },
        accessToken: 'access-token',
        refreshToken: 'refresh-token',
        expiresIn: 3600,
      })

      useAuthStore.getState().updateUser({ mustChangePassword: false })

      const state = useAuthStore.getState()
      expect(state.user?.mustChangePassword).toBe(false)
      // Other fields preserved
      expect(state.user?.username).toBe('admin')
      expect(state.user?.rol).toBe('admin')
      expect(state.user?.id).toBe(1)
    })

    it('updateUser_noops_when_user_null', () => {
      // user is null — should not throw
      expect(() => useAuthStore.getState().updateUser({ mustChangePassword: false })).not.toThrow()
      expect(useAuthStore.getState().user).toBeNull()
    })

    it('updateUser_can_patch_username', () => {
      useAuthStore.getState().setAuth({
        user: adminUser,
        accessToken: 'access-token',
        refreshToken: 'refresh-token',
        expiresIn: 3600,
      })

      useAuthStore.getState().updateUser({ username: 'new-admin' })

      expect(useAuthStore.getState().user?.username).toBe('new-admin')
    })
  })

  describe('clearAuth', () => {
    it('F-04-03: clearAuth → user = null (permisos se limpian con el user)', () => {
      useAuthStore.getState().setAuth({
        user: { id: 1, username: 'admin', nombre: 'Admin', rol: 'admin', permisos: ['administracion:usuarios:gestionar'], mustChangePassword: false },
        accessToken: 'access-token',
        refreshToken: 'refresh-token',
        expiresIn: 3600,
      })

      useAuthStore.getState().clearAuth()

      const state = useAuthStore.getState()
      expect(state.user).toBeNull()
    })

    it('clearAuth_removesAllFields', () => {
      useAuthStore.getState().setAuth({
        user: adminUser,
        accessToken: 'access-token',
        refreshToken: 'refresh-token',
        expiresIn: 3600,
      })

      useAuthStore.getState().clearAuth()

      const state = useAuthStore.getState()
      expect(state.user).toBeNull()
      expect(state.accessToken).toBeNull()
      expect(state.refreshToken).toBeNull()
      expect(state.expiresAt).toBeNull()
    })
  })

  describe('updateAccess', () => {
    it('updateAccess_updatesOnlyTokens_preservesUser', () => {
      useAuthStore.getState().setAuth({
        user: adminUser,
        accessToken: 'old-access',
        refreshToken: 'old-refresh',
        expiresIn: 3600,
      })

      const newExpiresAt = Date.now() + 7200 * 1000
      useAuthStore.getState().updateAccess('new-access', 'new-refresh', newExpiresAt)

      const state = useAuthStore.getState()
      expect(state.accessToken).toBe('new-access')
      expect(state.refreshToken).toBe('new-refresh')
      expect(state.expiresAt).toBe(newExpiresAt)
      // user should be preserved
      expect(state.user).toEqual(adminUser)
    })
  })

  describe('logout (async)', () => {
    it('logout_callsApi_thenClearsAuth', async () => {
      // Set up auth state with a token so logout() will try to call the API
      useAuthStore.getState().setAuth({
        user: adminUser,
        accessToken: 'access-token',
        refreshToken: 'refresh-token',
        expiresIn: 3600,
      })

      // logout() does a lazy import of authApi and calls logout()
      // The API call may succeed or fail (we don't control the real import here),
      // but clearAuth() is ALWAYS called after regardless.
      await useAuthStore.getState().logout()

      const state = useAuthStore.getState()
      expect(state.user).toBeNull()
      expect(state.accessToken).toBeNull()
      expect(state.refreshToken).toBeNull()
      expect(state.expiresAt).toBeNull()
    })

    it('logout_apiFails_stillClearsAuth', async () => {
      useAuthStore.getState().setAuth({
        user: adminUser,
        accessToken: 'access-token',
        refreshToken: 'refresh-token',
        expiresIn: 3600,
      })

      // Should NOT throw even if the dynamic import fails
      let threw = false
      try {
        await useAuthStore.getState().logout()
      } catch {
        threw = true
      }
      expect(threw).toBe(false)

      const state = useAuthStore.getState()
      expect(state.user).toBeNull()
      expect(state.accessToken).toBeNull()
    })
  })

  describe('legacy logout compatibility (via clearAuth)', () => {
    it('clearAuth clears user and accessToken from state', () => {
      useAuthStore.getState().setAuth({
        user: adminUser,
        accessToken: 'some-token',
        refreshToken: 'some-refresh',
        expiresIn: 3600,
      })

      useAuthStore.getState().clearAuth()

      const state = useAuthStore.getState()
      expect(state.user).toBeNull()
      expect(state.accessToken).toBeNull()
    })

    it('clearAuth removes auth-storage from localStorage', () => {
      useAuthStore.getState().setAuth({
        user: adminUser,
        accessToken: 'some-token',
        refreshToken: 'some-refresh',
        expiresIn: 3600,
      })

      useAuthStore.getState().clearAuth()

      const stored = localStorage.getItem('auth-storage')
      if (stored !== null) {
        const parsed = JSON.parse(stored)
        expect(parsed.state.user).toBeNull()
        expect(parsed.state.accessToken).toBeNull()
      }
    })
  })
})
