import { Injectable, Logger } from '@nestjs/common'
import { InjectModel } from '@nestjs/mongoose'
import { Model } from 'mongoose'
import { Progress, ProgressDocument } from '../schemas/progress.schema'

export interface DimensionSet {
  logic: number
  expression: number
  professionalism: number
  stability: number
}

export interface PositionBenchmark {
  position: string
  sampleCount: number
  p25: DimensionSet
  p50: DimensionSet
  p75: DimensionSet
  p90: DimensionSet
  avg: DimensionSet
}

@Injectable()
export class BenchmarkService {
  private readonly logger = new Logger(BenchmarkService.name)
  private cache: Record<string, PositionBenchmark> = {}
  private cacheTime = 0
  private readonly CACHE_TTL = 5 * 60 * 1000

  constructor(
    @InjectModel(Progress.name) private progressModel: Model<ProgressDocument>,
  ) {}

  private percentile(sorted: number[], p: number): number {
    if (sorted.length === 0) return 0
    const idx = (p / 100) * (sorted.length - 1)
    const lo = Math.floor(idx)
    const hi = Math.ceil(idx)
    if (lo === hi) return sorted[lo]
    return sorted[lo] + (sorted[hi] - sorted[lo]) * (idx - lo)
  }

  async getBenchmark(position?: string): Promise<PositionBenchmark | Record<string, PositionBenchmark>> {
    if (Date.now() - this.cacheTime > this.CACHE_TTL) {
      await this.refreshCache()
    }
    if (position) return this.cache[position] || this.getDefaultBenchmark(position)
    return this.cache
  }

  async getUserPercentile(userId: string, position?: string): Promise<{
    percentile: DimensionSet
    userScores: DimensionSet
    benchmark: PositionBenchmark
  } | null> {
    const progress = await this.progressModel.findOne({ userId }).exec()
    if (!progress || progress.completedInterviews === 0) return null

    const userScores: DimensionSet = {
      logic: progress.avgLogic || 0,
      expression: progress.avgExpression || 0,
      professionalism: progress.avgProfessionalism || 0,
      stability: progress.avgStability || 0,
    }

    const targetPosition = position || (progress.recentScores.length > 0
      ? progress.recentScores[progress.recentScores.length - 1].position : '')
    const benchmark = await this.getBenchmark(targetPosition)
    const bm = 'p50' in benchmark ? benchmark as PositionBenchmark : this.getDefaultBenchmark(targetPosition)

    const allScores = await this.getAllScoresForPosition(targetPosition)

    const percentile: DimensionSet = {
      logic: this.calcPercentile(allScores.logic, userScores.logic),
      expression: this.calcPercentile(allScores.expression, userScores.expression),
      professionalism: this.calcPercentile(allScores.professionalism, userScores.professionalism),
      stability: this.calcPercentile(allScores.stability, userScores.stability),
    }

    return { percentile, userScores, benchmark: bm }
  }

  private calcPercentile(sorted: number[], score: number): number {
    if (sorted.length === 0) return 50
    const below = sorted.filter(s => s < score).length
    return Math.round((below / sorted.length) * 100)
  }

  private async getAllScoresForPosition(position: string): Promise<Record<string, number[]>> {
    const allProgress = await this.progressModel.find({}).select('recentScores').lean().exec()
    const logic: number[] = []
    const expression: number[] = []
    const professionalism: number[] = []
    const stability: number[] = []

    for (const p of allProgress) {
      const scores = (p.recentScores || []) as Array<{
        position: string
        dimensions: { logic: number; expression: number; professionalism: number; stability: number }
      }>
      for (const s of scores) {
        if (s.position === position && s.dimensions) {
          logic.push(s.dimensions.logic)
          expression.push(s.dimensions.expression)
          professionalism.push(s.dimensions.professionalism)
          stability.push(s.dimensions.stability)
        }
      }
    }

    logic.sort((a, b) => a - b)
    expression.sort((a, b) => a - b)
    professionalism.sort((a, b) => a - b)
    stability.sort((a, b) => a - b)

    return { logic, expression, professionalism, stability }
  }

  private async refreshCache() {
    try {
      const allProgress = await this.progressModel.find({}).select('recentScores').lean().exec()
      const positionScores: Record<string, Record<string, number[]>> = {}

      for (const p of allProgress) {
        const scores = (p.recentScores || []) as Array<{
          position: string
          dimensions: { logic: number; expression: number; professionalism: number; stability: number }
        }>
        for (const s of scores) {
          if (!s.position || !s.dimensions) continue
          if (!positionScores[s.position]) {
            positionScores[s.position] = { logic: [], expression: [], professionalism: [], stability: [] }
          }
          positionScores[s.position].logic.push(s.dimensions.logic)
          positionScores[s.position].expression.push(s.dimensions.expression)
          positionScores[s.position].professionalism.push(s.dimensions.professionalism)
          positionScores[s.position].stability.push(s.dimensions.stability)
        }
      }

      const newCache: Record<string, PositionBenchmark> = {}
      for (const [pos, scores] of Object.entries(positionScores)) {
        for (const key of Object.keys(scores)) {
          scores[key].sort((a, b) => a - b)
        }
        const n = scores.logic.length
        newCache[pos] = {
          position: pos,
          sampleCount: n,
          p25: {
            logic: this.percentile(scores.logic, 25),
            expression: this.percentile(scores.expression, 25),
            professionalism: this.percentile(scores.professionalism, 25),
            stability: this.percentile(scores.stability, 25),
          },
          p50: {
            logic: this.percentile(scores.logic, 50),
            expression: this.percentile(scores.expression, 50),
            professionalism: this.percentile(scores.professionalism, 50),
            stability: this.percentile(scores.stability, 50),
          },
          p75: {
            logic: this.percentile(scores.logic, 75),
            expression: this.percentile(scores.expression, 75),
            professionalism: this.percentile(scores.professionalism, 75),
            stability: this.percentile(scores.stability, 75),
          },
          p90: {
            logic: this.percentile(scores.logic, 90),
            expression: this.percentile(scores.expression, 90),
            professionalism: this.percentile(scores.professionalism, 90),
            stability: this.percentile(scores.stability, 90),
          },
          avg: {
            logic: Math.round(scores.logic.reduce((a, b) => a + b, 0) / n),
            expression: Math.round(scores.expression.reduce((a, b) => a + b, 0) / n),
            professionalism: Math.round(scores.professionalism.reduce((a, b) => a + b, 0) / n),
            stability: Math.round(scores.stability.reduce((a, b) => a + b, 0) / n),
          },
        }
      }

      this.cache = newCache
      this.cacheTime = Date.now()
      this.logger.log(`Benchmark cache refreshed: ${Object.keys(newCache).length} positions`)
    } catch (e) {
      this.logger.error(`Failed to refresh benchmark cache: ${(e as Error).message}`)
    }
  }

  private getDefaultBenchmark(position: string): PositionBenchmark {
    return {
      position,
      sampleCount: 0,
      p25: { logic: 60, expression: 58, professionalism: 60, stability: 58 },
      p50: { logic: 72, expression: 70, professionalism: 72, stability: 68 },
      p75: { logic: 82, expression: 80, professionalism: 82, stability: 78 },
      p90: { logic: 90, expression: 88, professionalism: 90, stability: 85 },
      avg: { logic: 72, expression: 70, professionalism: 72, stability: 68 },
    }
  }
}
