import { Test, TestingModule } from '@nestjs/testing'
import { getModelToken } from '@nestjs/mongoose'
import { BenchmarkService, PositionBenchmark, DimensionSet } from './benchmark.service'

describe('BenchmarkService', () => {
  let service: BenchmarkService
  let mockProgressModel: any

  beforeEach(async () => {
    mockProgressModel = {
      find: jest.fn().mockReturnThis(),
      select: jest.fn().mockReturnThis(),
      lean: jest.fn().mockReturnThis(),
      exec: jest.fn().mockResolvedValue([]),
      findOne: jest.fn().mockReturnThis(),
    }

    const module: TestingModule = await Test.createTestingModule({
      providers: [
        BenchmarkService,
        { provide: getModelToken('Progress'), useValue: mockProgressModel },
      ],
    }).compile()

    service = module.get<BenchmarkService>(BenchmarkService)
  })

  afterEach(() => {
    jest.clearAllMocks()
  })

  describe('percentile', () => {
    it('should return 0 for empty array', () => {
      const result = (service as any).percentile([], 50)
      expect(result).toBe(0)
    })

    it('should return the exact value for p50 with odd count', () => {
      const result = (service as any).percentile([10, 20, 30, 40, 50], 50)
      expect(result).toBe(30)
    })

    it('should interpolate for p50 with even count', () => {
      const result = (service as any).percentile([10, 20, 30, 40], 50)
      expect(result).toBe(25)
    })

    it('should return min for p0', () => {
      const result = (service as any).percentile([5, 15, 25], 0)
      expect(result).toBe(5)
    })

    it('should return max for p100', () => {
      const result = (service as any).percentile([5, 15, 25], 100)
      expect(result).toBe(25)
    })
  })

  describe('calcPercentile', () => {
    it('should return 50 for empty array', () => {
      const result = (service as any).calcPercentile([], 75)
      expect(result).toBe(50)
    })

    it('should return 0 if score is below all', () => {
      const result = (service as any).calcPercentile([50, 60, 70], 40)
      expect(result).toBe(0)
    })

    it('should return 100 if score is above all', () => {
      const result = (service as any).calcPercentile([50, 60, 70], 80)
      expect(result).toBe(100)
    })

    it('should return correct percentile', () => {
      const result = (service as any).calcPercentile([50, 60, 70, 80, 90], 75)
      expect(result).toBe(60)
    })
  })

  describe('getDefaultBenchmark', () => {
    it('should return default values for given position', () => {
      const result = service['getDefaultBenchmark']('前端工程师')
      expect(result.position).toBe('前端工程师')
      expect(result.sampleCount).toBe(0)
      expect(result.p50.logic).toBe(72)
      expect(result.p50.expression).toBe(70)
      expect(result.p50.professionalism).toBe(72)
      expect(result.p50.stability).toBe(68)
    })

    it('should have all required fields', () => {
      const result = service['getDefaultBenchmark']('测试')
      const keys: (keyof PositionBenchmark)[] = ['position', 'sampleCount', 'p25', 'p50', 'p75', 'p90', 'avg']
      for (const key of keys) {
        expect(result).toHaveProperty(key)
      }
    })
  })

  describe('getBenchmark', () => {
    it('should return default benchmark when no data in cache', async () => {
      const result = await service.getBenchmark('前端工程师')
      expect(result).toBeDefined()
      expect((result as PositionBenchmark).position).toBe('前端工程师')
    })

    it('should return all benchmarks when no position specified', async () => {
      const result = await service.getBenchmark()
      expect(result).toBeDefined()
      expect(typeof result).toBe('object')
    })
  })

  describe('getUserPercentile', () => {
    it('should return null when user has no progress', async () => {
      mockProgressModel.findOne.mockReturnValueOnce({
        exec: jest.fn().mockResolvedValue(null),
      })
      const result = await service.getUserPercentile('nonexistent')
      expect(result).toBeNull()
    })

    it('should return null when user has 0 completed interviews', async () => {
      mockProgressModel.findOne.mockReturnValueOnce({
        exec: jest.fn().mockResolvedValue({ completedInterviews: 0 }),
      })
      const result = await service.getUserPercentile('user1')
      expect(result).toBeNull()
    })
  })
})
