import { Injectable } from '@nestjs/common';
import { PrismaService } from '@core/database/prisma/prisma.service';
import { Prisma } from '@prisma/client';
import { PERFORMANCE_ERROR_CODES } from '../constants/error-codes';
import { GRADE_COLORS, GRADE_NAMES } from '../constants/grade-defaults';
import { BusinessException } from '@common/exceptions/business.exception';

/**
 * 绩效校准服务
 * 负责校准看板、等级调整、分布统计
 */
@Injectable()
export class CalibrationService {
  constructor(private readonly prisma: PrismaService) {}

  // ==================== 周期级校准 ====================

  async getCalibrationOverview(cycleId: string) {
    // 验证周期
    const cycle = await this.prisma.performanceCycle.findFirst({
      where: { id: cycleId, deletedAt: null },
    });
    if (!cycle) {
      throw new BusinessException(
        PERFORMANCE_ERROR_CODES.CYCLE_NOT_FOUND.message,
        PERFORMANCE_ERROR_CODES.CYCLE_NOT_FOUND.code,
        PERFORMANCE_ERROR_CODES.CYCLE_NOT_FOUND.httpStatus,
      );
    }

    const results = await this.prisma.performanceResult.findMany({
      where: { cycleId, deletedAt: null, totalScore: { not: null } },
      select: {
        id: true, employeeId: true, kpiScore: true, e360Score: true,
        totalScore: true, gradeCode: true, remarks: true,
      },
    });

    const employeeIds = results.map((r) => r.employeeId);

    // 以下 4 个查询互相独立，并发执行
    const [users, userDepartments, adjustmentLogs, kpiAssignments] = await Promise.all([
      this.prisma.user.findMany({
        where: { id: { in: employeeIds } },
        select: { id: true, displayName: true },
      }),
      this.prisma.userDepartment.findMany({
        where: { userId: { in: employeeIds }, leftAt: null },
        include: { department: { select: { id: true, name: true } } },
      }),
      this.prisma.gradeAdjustmentLog.findMany({
        where: { result: { cycleId, deletedAt: null }, deletedAt: null },
        orderBy: { adjustedAt: 'desc' },
        include: { result: { select: { employeeId: true } } },
      }),
      this.prisma.kpiAssignment.findMany({
        where: { cycleId, deletedAt: null, employeeId: { in: employeeIds } },
        include: { assessment: { where: { deletedAt: null } } },
      }),
    ]);

    const userMap = new Map(users.map((u) => [u.id, u.displayName]));
    const deptMap = new Map(
      userDepartments.map((ud) => [ud.userId, ud.department?.name || '']),
    );
    const latestAdjustmentMap = new Map<string, string>();
    for (const log of adjustmentLogs) {
      const empId = log.result.employeeId;
      if (!latestAdjustmentMap.has(empId)) {
        latestAdjustmentMap.set(empId, log.newGradeCode);
      }
    }
    const kpiByEmp = new Map<string, { count: number; selfWeighted: number; managerWeighted: number; totalWeight: number }>();
    for (const ka of kpiAssignments) {
      const emp = kpiByEmp.get(ka.employeeId) || { count: 0, selfWeighted: 0, managerWeighted: 0, totalWeight: 0 };
      emp.count++;
      const w = Number(ka.weight) || 0;
      emp.totalWeight += w;
      if (ka.assessment) {
        emp.selfWeighted += (Number(ka.assessment.selfScore) || 0) * w;
        emp.managerWeighted += (Number(ka.assessment.managerScore) || 0) * w;
      }
      kpiByEmp.set(ka.employeeId, emp);
    }

    const items = results.map((r) => {
      const kpi = kpiByEmp.get(r.employeeId);
      const selfW = kpi && kpi.totalWeight > 0 ? Math.round(kpi.selfWeighted / kpi.totalWeight * 10) / 10 : null;
      const mgrW = kpi && kpi.totalWeight > 0 ? Math.round(kpi.managerWeighted / kpi.totalWeight * 10) / 10 : null;
      const empAssignments = kpiAssignments
        .filter((ka) => ka.employeeId === r.employeeId)
        .map((ka) => ({
          id: ka.id,
          name: ka.name,
          weight: Number(ka.weight) || 0,
          assessment: ka.assessment ? {
            selfScore: ka.assessment.selfScore != null ? Number(ka.assessment.selfScore) : null,
            managerScore: ka.assessment.managerScore != null ? Number(ka.assessment.managerScore) : null,
            selfComment: ka.assessment.selfComment || null,
            managerComment: ka.assessment.managerComment || null,
          } : null,
        }));
      return {
        id: r.id,
        employeeId: r.employeeId,
        name: userMap.get(r.employeeId) || '',
        department: deptMap.get(r.employeeId) || '',
        kpiCount: kpi?.count || 0,
        selfWeightedScore: selfW,
        managerWeightedScore: mgrW,
        kpiScore: r.kpiScore ? Number(r.kpiScore) : mgrW,
        e360Score: r.e360Score ? Number(r.e360Score) : null,
        totalScore: Number(r.totalScore),
        originalGrade: r.gradeCode || '',
        calibratedGrade: latestAdjustmentMap.get(r.employeeId) || r.gradeCode || '',
        calibrationScore: Number(r.totalScore),
        calibrationNote: r.remarks || null,
        assignments: empAssignments,
      };
    });

    // 统计
    const scores = items.map((i) => i.totalScore).sort((a, b) => a - b);
    const totalEmployees = items.length;
    const avgScore = totalEmployees > 0 ? Math.round(scores.reduce((s, v) => s + v, 0) / totalEmployees * 10) / 10 : 0;
    const medianScore = totalEmployees > 0
      ? totalEmployees % 2 === 1
        ? scores[Math.floor(totalEmployees / 2)]
        : Math.round((scores[totalEmployees / 2 - 1] + scores[totalEmployees / 2]) / 2 * 10) / 10
      : 0;

    // 等级分布
    const gradeOrder = ['S', 'A', 'B', 'C', 'D'];
    const gradeCount = new Map<string, number>();
    for (const item of items) {
      const g = item.calibratedGrade || item.originalGrade;
      gradeCount.set(g, (gradeCount.get(g) || 0) + 1);
    }
    const gradeDistribution = gradeOrder.map((code) => ({
      code,
      name: code,
      count: gradeCount.get(code) || 0,
      color: GRADE_COLORS[code] || '#646a73',
    }));

    return { cycleId, totalEmployees, avgScore, medianScore, gradeDistribution, items };
  }

  async saveCalibrationAdjustment(data: {
    cycleId: string;
    employeeId: string;
    calibratedGrade?: string;
    calibrationScore?: number;
    note?: string;
  }, adjustedBy: string) {
    // 查找 PerformanceResult
    const result = await this.prisma.performanceResult.findFirst({
      where: {
        cycleId: data.cycleId,
        employeeId: data.employeeId,
        deletedAt: null,
      },
    });
    if (!result) {
      throw new BusinessException(
        PERFORMANCE_ERROR_CODES.RESULT_NOT_FOUND.message,
        PERFORMANCE_ERROR_CODES.RESULT_NOT_FOUND.code,
        PERFORMANCE_ERROR_CODES.RESULT_NOT_FOUND.httpStatus,
      );
    }

    const previousGradeCode = result.gradeCode || '';
    const updateData: any = {};

    if (data.calibratedGrade) {
      updateData.gradeCode = data.calibratedGrade;
      // 同步更新等级名称
      updateData.gradeName = GRADE_NAMES[data.calibratedGrade] || data.calibratedGrade;
    }
    if (data.calibrationScore !== undefined) {
      updateData.totalScore = data.calibrationScore;
    }
    if (data.note !== undefined) {
      updateData.remarks = data.note;
    }

    await this.prisma.$transaction(async (tx) => {
      await tx.performanceResult.update({
        where: { id: result.id },
        data: updateData,
      });

      // 如果有等级变更，创建调整日志
      if (data.calibratedGrade) {
        await tx.gradeAdjustmentLog.create({
          data: {
            resultId: result.id,
            previousGradeCode,
            newGradeCode: data.calibratedGrade,
            reason: data.note || '周期级校准调整',
            adjustedBy,
          },
        });
      }
    });

    return { success: true };
  }
}
