import { Injectable, BadRequestException, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { OnEvent } from '@nestjs/event-emitter';
import { Achievement } from './achievement.entity';
import { PlayerAchievement } from './player-achievement.entity';
import { Character } from '../character/entities/character.entity';

export interface AchievementCheckEvent {
  characterId: string;
  type: string;    // matches achievement.criteriaType
  increment: number;
  absolute?: number; // for 'level_reached', 'gold_accumulated' — set progress directly
}

@Injectable()
export class AchievementService {
  constructor(
    @InjectRepository(Achievement)
    private readonly achievementRepo: Repository<Achievement>,
    @InjectRepository(PlayerAchievement)
    private readonly playerAchievementRepo: Repository<PlayerAchievement>,
    @InjectRepository(Character)
    private readonly characterRepo: Repository<Character>,
  ) {}

  findAll() {
    return this.achievementRepo.find({ order: { category: 'ASC', criteriaValue: 'ASC' } });
  }

  async getMyProgress(characterId: string) {
    const achievements = await this.achievementRepo.find();
    const playerAchievements = await this.playerAchievementRepo.find({
      where: { characterId },
    });

    const progressMap = new Map(playerAchievements.map((pa) => [pa.achievementId, pa]));

    return achievements.map((a) => {
      const pa = progressMap.get(a.id);
      return {
        ...a,
        progress: pa?.progress ?? 0,
        unlocked: pa?.unlocked ?? false,
        unlockedAt: pa?.unlockedAt ?? null,
        claimed: pa?.claimed ?? false,
        percentage: Math.min(100, Math.floor(((pa?.progress ?? 0) / a.criteriaValue) * 100)),
      };
    });
  }

  async claim(achievementId: string, characterId: string) {
    const pa = await this.playerAchievementRepo.findOne({
      where: { achievementId, characterId },
      relations: ['achievement'],
    });
    if (!pa) throw new NotFoundException('Succès introuvable');
    if (!pa.unlocked) throw new BadRequestException('Succès pas encore débloqué');
    if (pa.claimed) throw new BadRequestException('Récompense déjà réclamée');

    pa.claimed = true;
    await this.playerAchievementRepo.save(pa);

    // Credit gold reward
    const character = await this.characterRepo.findOne({ where: { id: characterId } });
    if (character && pa.achievement.rewardGold > 0) {
      character.gold += pa.achievement.rewardGold;
      await this.characterRepo.save(character);
    }

    return {
      claimed: true,
      achievement: pa.achievement.name,
      rewardGold: pa.achievement.rewardGold,
      rewardTitle: pa.achievement.rewardTitle,
    };
  }

  @OnEvent('achievement.check')
  async handleAchievementCheck(event: AchievementCheckEvent) {
    const { characterId, type, increment, absolute } = event;

    // Find all achievements matching this criteria type
    const achievements = await this.achievementRepo.find({
      where: { criteriaType: type },
    });
    if (!achievements.length) return;

    for (const achievement of achievements) {
      // Get or create player progress
      let pa = await this.playerAchievementRepo.findOne({
        where: { characterId, achievementId: achievement.id },
      });

      if (!pa) {
        pa = this.playerAchievementRepo.create({
          characterId,
          achievementId: achievement.id,
          progress: 0,
          unlocked: false,
          claimed: false,
        });
      }

      if (pa.unlocked) continue; // already unlocked, skip

      // Update progress
      if (absolute !== undefined) {
        pa.progress = Math.max(pa.progress, absolute);
      } else {
        pa.progress += increment;
      }

      // Check unlock
      if (pa.progress >= achievement.criteriaValue) {
        pa.unlocked = true;
        pa.unlockedAt = new Date();
      }

      await this.playerAchievementRepo.save(pa);
    }
  }
}
