import { describe, expect, it } from 'vitest'

import {
  AppError,
  ErrorType,
  isAppError,
  getErrorMessage,
  isFormValidationError,
} from './index'

describe('lib/errors', () => {
  describe('ErrorType', () => {
    it('should have all error type constants', () => {
      expect(ErrorType.BUSINESS).toBe('BUSINESS')
      expect(ErrorType.NETWORK).toBe('NETWORK')
      expect(ErrorType.AUTH).toBe('AUTH')
      expect(ErrorType.FORBIDDEN).toBe('FORBIDDEN')
      expect(ErrorType.NOT_FOUND).toBe('NOT_FOUND')
      expect(ErrorType.VALIDATION).toBe('VALIDATION')
      expect(ErrorType.UNKNOWN).toBe('UNKNOWN')
    })
  })

  describe('AppError', () => {
    describe('constructor', () => {
      it('should create an AppError with required fields', () => {
        const error = new AppError(1001, 'Test error')

        expect(error.code).toBe(1001)
        expect(error.message).toBe('Test error')
        expect(error.name).toBe('AppError')
        expect(error.status).toBe(500) // default
        expect(error.type).toBe(ErrorType.BUSINESS) // default
      })

      it('should create an AppError with options', () => {
        const cause = new Error('Original error')
        const error = new AppError(1001, 'Test error', {
          status: 400,
          type: ErrorType.VALIDATION,
          cause,
        })

        expect(error.status).toBe(400)
        expect(error.type).toBe(ErrorType.VALIDATION)
        expect(error.cause).toBe(cause)
      })
    })

    describe('fromResponse', () => {
      it('should create AUTH error for 401 status', () => {
        const error = AppError.fromResponse({ code: 401, message: 'Unauthorized' }, 401)

        expect(error.type).toBe(ErrorType.AUTH)
        expect(error.status).toBe(401)
        expect(error.code).toBe(401)
      })

      it('should create FORBIDDEN error for 403 status', () => {
        const error = AppError.fromResponse({ code: 403, message: 'Forbidden' }, 403)

        expect(error.type).toBe(ErrorType.FORBIDDEN)
        expect(error.status).toBe(403)
      })

      it('should create NOT_FOUND error for 404 status', () => {
        const error = AppError.fromResponse({ code: 404, message: 'Not found' }, 404)

        expect(error.type).toBe(ErrorType.NOT_FOUND)
        expect(error.status).toBe(404)
      })

      it('should create NETWORK error for 500+ status', () => {
        const error = AppError.fromResponse({ code: 500, message: 'Server error' }, 500)

        expect(error.type).toBe(ErrorType.NETWORK)
        expect(error.status).toBe(500)
      })

      it('should create BUSINESS error for other status codes', () => {
        const error = AppError.fromResponse({ code: 1001, message: 'Business error' }, 200)

        expect(error.type).toBe(ErrorType.BUSINESS)
        expect(error.code).toBe(1001)
      })
    })

    describe('static factory methods', () => {
      it('should create network error', () => {
        const cause = new Error('Network failed')
        const error = AppError.network('Network error', cause)

        expect(error.type).toBe(ErrorType.NETWORK)
        expect(error.status).toBe(0)
        expect(error.code).toBe(0)
        expect(error.cause).toBe(cause)
      })

      it('should create auth error with default message', () => {
        const error = AppError.auth()

        expect(error.type).toBe(ErrorType.AUTH)
        expect(error.status).toBe(401)
        expect(error.message).toBe('请先登录')
      })

      it('should create auth error with custom message', () => {
        const error = AppError.auth('Token expired')

        expect(error.message).toBe('Token expired')
      })

      it('should create forbidden error with default message', () => {
        const error = AppError.forbidden()

        expect(error.type).toBe(ErrorType.FORBIDDEN)
        expect(error.status).toBe(403)
        expect(error.message).toBe('无权限访问')
      })

      it('should create forbidden error with custom message', () => {
        const error = AppError.forbidden('Admin only')

        expect(error.message).toBe('Admin only')
      })

      it('should create validation error', () => {
        const error = AppError.validation('Invalid input')

        expect(error.type).toBe(ErrorType.VALIDATION)
        expect(error.status).toBe(400)
        expect(error.message).toBe('Invalid input')
      })
    })

    describe('instance methods', () => {
      it('should check if auth error', () => {
        const authError = AppError.auth()
        const otherError = new AppError(500, 'Server error')

        expect(authError.isAuthError()).toBe(true)
        expect(otherError.isAuthError()).toBe(false)
      })

      it('should check if forbidden error', () => {
        const forbiddenError = AppError.forbidden()
        const otherError = new AppError(500, 'Server error')

        expect(forbiddenError.isForbidden()).toBe(true)
        expect(otherError.isForbidden()).toBe(false)
      })

      it('should check if network error', () => {
        const networkError = AppError.network('Network failed')
        const otherError = new AppError(500, 'Server error')

        expect(networkError.isNetworkError()).toBe(true)
        expect(otherError.isNetworkError()).toBe(false)
      })
    })

    describe('getUserMessage', () => {
      it('should return user-friendly message for NETWORK type', () => {
        const error = AppError.network('Network failed')
        expect(error.getUserMessage()).toBe('网络连接失败，请检查网络后重试')
      })

      it('should return user-friendly message for AUTH type', () => {
        const error = AppError.auth('Token expired')
        expect(error.getUserMessage()).toBe('登录已过期，请重新登录')
      })

      it('should return user-friendly message for FORBIDDEN type', () => {
        const error = AppError.forbidden('No access')
        expect(error.getUserMessage()).toBe('您没有权限执行此操作')
      })

      it('should return user-friendly message for NOT_FOUND type', () => {
        const error = AppError.fromResponse({ code: 404, message: 'Not found' }, 404)
        expect(error.getUserMessage()).toBe('请求的资源不存在')
      })

      it('should return original message for VALIDATION type', () => {
        const error = AppError.validation('邮箱格式不正确')
        expect(error.getUserMessage()).toBe('邮箱格式不正确')
      })

      it('should return original message for BUSINESS type', () => {
        const error = new AppError(1001, '用户名已存在')
        expect(error.getUserMessage()).toBe('用户名已存在')
      })

      it('should return fallback for empty message', () => {
        const error = new AppError(0, '', { type: ErrorType.UNKNOWN })
        expect(error.getUserMessage()).toBe('操作失败，请稍后重试')
      })
    })
  })

  describe('isAppError', () => {
    it('should return true for AppError instances', () => {
      const error = new AppError(1001, 'Test error')
      expect(isAppError(error)).toBe(true)
    })

    it('should return false for Error instances', () => {
      const error = new Error('Test error')
      expect(isAppError(error)).toBe(false)
    })

    it('should return false for non-error values', () => {
      expect(isAppError('error')).toBe(false)
      expect(isAppError(123)).toBe(false)
      expect(isAppError(null)).toBe(false)
      expect(isAppError(undefined)).toBe(false)
    })
  })

  describe('getErrorMessage', () => {
    it('should return user message for AppError', () => {
      const error = AppError.auth('Token expired')
      expect(getErrorMessage(error, 'Fallback')).toBe('登录已过期，请重新登录')
    })

    it('should return message for Error instances', () => {
      const error = new Error('Test error')
      expect(getErrorMessage(error, 'Fallback')).toBe('Test error')
    })

    it('should return fallback for non-error values', () => {
      expect(getErrorMessage('string', 'Fallback')).toBe('Fallback')
      expect(getErrorMessage(null, 'Fallback')).toBe('Fallback')
      expect(getErrorMessage(undefined, 'Fallback')).toBe('Fallback')
      expect(getErrorMessage(123, 'Fallback')).toBe('Fallback')
    })
  })

  describe('isFormValidationError', () => {
    it('should return true for form validation errors', () => {
      const error = { errorFields: [{ name: 'email' }] }
      expect(isFormValidationError(error)).toBe(true)
    })

    it('should return false for empty errorFields', () => {
      const error = { errorFields: [] }
      expect(isFormValidationError(error)).toBe(true) // Empty array is still valid
    })

    it('should return false for non-array errorFields', () => {
      const error = { errorFields: 'not an array' }
      expect(isFormValidationError(error)).toBe(false)
    })

    it('should return false for objects without errorFields', () => {
      const error = { message: 'Error' }
      expect(isFormValidationError(error)).toBe(false)
    })

    it('should return false for non-object values', () => {
      expect(isFormValidationError('error')).toBe(false)
      expect(isFormValidationError(123)).toBe(false)
      expect(isFormValidationError(null)).toBe(false)
      expect(isFormValidationError(undefined)).toBe(false)
    })
  })
})
