import { Injectable, Logger } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User, WorkSchedule } from '../users/entities/user.entity';
import { LeaveType } from './entities/leave-type.entity';
import { LeaveBalance } from './entities/leave-balance.entity';
import { AuditLog } from '../audit/entities/audit-log.entity';
import { computeAcquiredDays } from './utils/seniority.calculator';

@Injectable()
export class LeavesAcquisitionService {
  private readonly logger = new Logger(LeavesAcquisitionService.name);

  constructor(
    @InjectRepository(User)
    private readonly userRepo: Repository<User>,

    @InjectRepository(LeaveType)
    private readonly leaveTypeRepo: Repository<LeaveType>,

    @InjectRepository(LeaveBalance)
    private readonly leaveBalanceRepo: Repository<LeaveBalance>,

    @InjectRepository(AuditLog)
    private readonly auditLogRepo: Repository<AuditLog>,
  ) {}

  // ─── Acquisition annuelle complète (lancée au 1er janvier) ──────────────────
  async runAnnualAcquisition(year?: number): Promise<{ updated: number; created: number }> {
    const targetYear = year ?? new Date().getFullYear();
    const users      = await this.userRepo.find({ where: { is_active: true } });
    const leaveTypes = await this.leaveTypeRepo.find({ where: { is_active: true, deducts_balance: true } });

    let updated = 0;
    let created = 0;

    for (const user of users) {
      for (const leaveType of leaveTypes) {
        // Seuls les CP et RTT bénéficient de l'acquisition automatique
        if (!['CP', 'RTT'].includes(leaveType.code)) continue;

        let acquired = 0;

        if (leaveType.code === 'CP') {
          acquired = computeAcquiredDays(
            new Date(user.hired_at),
            targetYear,
            user.work_schedule,
          );
        } else if (leaveType.code === 'RTT') {
          // RTT : 12 jours fixes pour 5j/semaine, proportionnel sinon
          acquired = user.work_schedule === WorkSchedule.FIVE_DAYS  ? 12 :
                     user.work_schedule === WorkSchedule.SIX_DAYS   ? 12 :
                     user.work_schedule === WorkSchedule.FOUR_DAYS  ? 0  : 0;
        }

        const existing = await this.leaveBalanceRepo.findOne({
          where: {
            user: { id: user.id },
            leave_type: { id: leaveType.id },
            year: targetYear,
          },
        });

        if (existing) {
          const oldAcquired = existing.acquired;
          await this.leaveBalanceRepo.update(existing.id, { acquired });

          await this.auditLogRepo.save(this.auditLogRepo.create({
            action: 'BALANCE_AUTO_ACQUIRED',
            entity_type: 'leave_balance',
            entity_id: existing.id,
            old_value: { acquired: oldAcquired },
            new_value: { acquired },
          }));

          updated++;
          this.logger.log(`↑ Mis à jour : ${user.email} / ${leaveType.code} → ${acquired}j`);
        } else {
          const balance = await this.leaveBalanceRepo.save(
            this.leaveBalanceRepo.create({
              user,
              leave_type: leaveType,
              year: targetYear,
              acquired,
              taken: 0,
              adjusted: 0,
            }),
          );

          await this.auditLogRepo.save(this.auditLogRepo.create({
            action: 'BALANCE_AUTO_ACQUIRED',
            entity_type: 'leave_balance',
            entity_id: balance.id,
            old_value: {},
            new_value: { acquired },
          }));

          created++;
          this.logger.log(`✓ Créé : ${user.email} / ${leaveType.code} → ${acquired}j`);
        }
      }
    }

    this.logger.log(`✅ Acquisition ${targetYear} terminée — ${created} créés, ${updated} mis à jour`);
    return { updated, created };
  }

  // ─── Acquisition mensuelle (lancée le 1er de chaque mois) ───────────────────
  async runMonthlyAcquisition(): Promise<void> {
    const now        = new Date();
    const year       = now.getFullYear();
    const users      = await this.userRepo.find({ where: { is_active: true } });
    const cpType     = await this.leaveTypeRepo.findOne({ where: { code: 'CP' } });

    if (!cpType) return;

    for (const user of users) {
      // Vérifier que le collaborateur était présent ce mois
      const hiredAt = new Date(user.hired_at);
      if (hiredAt > now) continue; // Pas encore embauché

      const balance = await this.leaveBalanceRepo.findOne({
        where: {
          user: { id: user.id },
          leave_type: { id: cpType.id },
          year,
        },
      });

      if (!balance) continue;

      // Recalculer le total acquis depuis le début de l'année
      const newAcquired = computeAcquiredDays(
        new Date(user.hired_at),
        year,
        user.work_schedule,
      );

      // Mettre à jour uniquement si différent
      if (+balance.acquired !== newAcquired) {
        await this.leaveBalanceRepo.update(balance.id, { acquired: newAcquired });
        this.logger.log(
          `↑ Acquisition mensuelle : ${user.email} → ${balance.acquired}j → ${newAcquired}j`
        );
      }
    }
  }

  // ─── Prévisualiser l'acquisition d'un collaborateur ─────────────────────────
  previewAcquisition(
    hiredAt: Date,
    schedule: WorkSchedule,
    year?: number,
  ): {
    year: number;
    schedule: WorkSchedule;
    acquired_cp: number;
    acquired_rtt: number;
    seniority_years: number;
    bonus_days: number;
  } {
    const targetYear = year ?? new Date().getFullYear();
    const acquired   = computeAcquiredDays(new Date(hiredAt), targetYear, schedule);

    // Recalculer pour extraire le détail
    const startOfYear  = new Date(targetYear, 0, 1);
    const from         = new Date(hiredAt) > startOfYear ? new Date(hiredAt) : startOfYear;
    const workedMonths = Math.min(
      (new Date(targetYear, 11, 31).getFullYear() - from.getFullYear()) * 12
      + (new Date(targetYear, 11, 31).getMonth() - from.getMonth()) + 1,
      12,
    );

    const basePerMonth  = schedule === WorkSchedule.FOUR_DAYS ? 2.0 : 2.5;
    const baseDays      = Math.min(workedMonths * basePerMonth,
                          schedule === WorkSchedule.FOUR_DAYS ? 24 : 30);

    const seniorityYears = Math.floor(
      ((startOfYear.getTime() - new Date(hiredAt).getTime()) / (1000 * 60 * 60 * 24 * 365))
    );
    const bonusDays = seniorityYears >= 15 ? 3 :
                      seniorityYears >= 10 ? 2 :
                      seniorityYears >= 5  ? 1 : 0;

    return {
      year: targetYear,
      schedule,
      acquired_cp:     acquired,
      acquired_rtt:    schedule === WorkSchedule.FOUR_DAYS ? 0 : 12,
      seniority_years: seniorityYears,
      bonus_days:      bonusDays,
    };
  }
}