import { describe, it, expect, beforeEach } from 'vitest'
import { http, HttpResponse } from 'msw'
import { server } from '@/test/mocks/server'
import { mockTokenResponse, mockUser } from '@/test/mocks/data'
import {
  login,
  register,
  logout,
  getMe,
  updateProfile,
  uploadAvatar,
  deleteAvatar,
  changePassword,
  setupMFA,
  verifyMFA,
  disableMFA,
} from '@/services/authService'

describe('authService', () => {
  beforeEach(() => {
    localStorage.clear()
  })

  // ---------- login ----------

  describe('login', () => {
    it('stores tokens and returns response', async () => {
      const result = await login({ email: 'admin@dak.de', password: 'password123' })

      expect(result).toEqual(mockTokenResponse)
      expect(localStorage.getItem('access_token')).toBe(mockTokenResponse.access_token)
      expect(localStorage.getItem('refresh_token')).toBe(mockTokenResponse.refresh_token)
    })

    it('throws on invalid credentials (401)', async () => {
      server.use(
        http.post('/api/auth/login', () => {
          return HttpResponse.json(
            { detail: 'Invalid credentials' },
            { status: 401 },
          )
        }),
      )

      await expect(
        login({ email: 'wrong@dak.de', password: 'wrong' }),
      ).rejects.toThrow()

      // Tokens should not be stored on failure
      expect(localStorage.getItem('access_token')).toBeNull()
      expect(localStorage.getItem('refresh_token')).toBeNull()
    })
  })

  // ---------- register ----------

  describe('register', () => {
    it('returns user data', async () => {
      const result = await register({
        username: 'new_user',
        email: 'new@dak.de',
        password: 'securePass123',
      })

      expect(result).toEqual({ user: mockUser })
    })
  })

  // ---------- logout ----------

  describe('logout', () => {
    it('clears tokens from localStorage', async () => {
      localStorage.setItem('access_token', 'some-token')
      localStorage.setItem('refresh_token', 'some-refresh')

      await logout()

      expect(localStorage.getItem('access_token')).toBeNull()
      expect(localStorage.getItem('refresh_token')).toBeNull()
    })

    it('clears tokens even if API call fails', async () => {
      localStorage.setItem('access_token', 'some-token')
      localStorage.setItem('refresh_token', 'some-refresh')

      server.use(
        http.post('/api/auth/logout', () => {
          return new HttpResponse(null, { status: 500 })
        }),
      )

      // logout() uses try/finally (no catch), so the error propagates
      // but the finally block still clears tokens
      try {
        await logout()
      } catch {
        // Expected: the 500 error propagates
      }

      expect(localStorage.getItem('access_token')).toBeNull()
      expect(localStorage.getItem('refresh_token')).toBeNull()
    })
  })

  // ---------- getMe ----------

  describe('getMe', () => {
    it('returns user profile', async () => {
      localStorage.setItem('access_token', 'valid-token')

      const result = await getMe()
      expect(result).toEqual(mockUser)
    })
  })

  // ---------- updateProfile ----------

  describe('updateProfile', () => {
    it('returns updated user', async () => {
      localStorage.setItem('access_token', 'valid-token')

      const updatedUser = { ...mockUser, first_name: 'Maximilian' }
      server.use(
        http.put('/api/auth/profile', () => {
          return HttpResponse.json(updatedUser)
        }),
      )

      const result = await updateProfile({ first_name: 'Maximilian' })
      expect(result).toEqual(updatedUser)
    })
  })

  // ---------- uploadAvatar ----------

  describe('uploadAvatar', () => {
    it('sends FormData and returns user', async () => {
      localStorage.setItem('access_token', 'valid-token')

      const file = new File(['avatar-content'], 'avatar.png', { type: 'image/png' })
      const result = await uploadAvatar(file)

      expect(result).toEqual({ ...mockUser, avatar_url: '/uploads/avatar-1.png' })
    })
  })

  // ---------- deleteAvatar ----------

  describe('deleteAvatar', () => {
    it('returns user with null avatar_url', async () => {
      localStorage.setItem('access_token', 'valid-token')

      const result = await deleteAvatar()
      expect(result).toEqual({ ...mockUser, avatar_url: null })
    })
  })

  // ---------- changePassword ----------

  describe('changePassword', () => {
    it('succeeds without error', async () => {
      localStorage.setItem('access_token', 'valid-token')

      await expect(
        changePassword({ old_password: 'oldPass', new_password: 'newPass123' }),
      ).resolves.toBeUndefined()
    })

    it('throws on wrong old password', async () => {
      localStorage.setItem('access_token', 'valid-token')

      server.use(
        http.post('/api/auth/change-password', () => {
          return HttpResponse.json(
            { detail: 'Wrong password' },
            { status: 400 },
          )
        }),
      )

      await expect(
        changePassword({ old_password: 'wrongPass', new_password: 'newPass123' }),
      ).rejects.toThrow()
    })
  })

  // ---------- MFA ----------

  describe('MFA', () => {
    it('setupMFA returns secret + qr_uri', async () => {
      localStorage.setItem('access_token', 'valid-token')

      const result = await setupMFA()
      expect(result).toEqual({
        secret: 'JBSWY3DPEHPK3PXP',
        qr_uri: 'otpauth://totp/DAK:admin@dak.de?secret=JBSWY3DPEHPK3PXP&issuer=DAK',
      })
    })

    it('verifyMFA succeeds', async () => {
      localStorage.setItem('access_token', 'valid-token')

      await expect(
        verifyMFA({ secret: 'JBSWY3DPEHPK3PXP', code: '123456' }),
      ).resolves.toBeUndefined()
    })

    it('disableMFA succeeds', async () => {
      localStorage.setItem('access_token', 'valid-token')

      await expect(disableMFA('myPassword')).resolves.toBeUndefined()
    })
  })
})
