import { Injectable, BadRequestException, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { DataSource, Repository } from 'typeorm';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { CharacterItem } from '../item/character-item.entity';
import { Character } from '../character/entities/character.entity';
import { User } from '../user/user.entity';

const MAX_FORGE_LEVEL = 5;
const FORGE_BONUS_PER_LEVEL = 2;

// Coût en or par niveau cible
const FORGE_GOLD_COST: Record<number, number> = {
  1: 50,
  2: 100,
  3: 200,
  4: 400,
  5: 700,
};

const FORGE_ENDURANCE_COST = 10;

// Risque d'échec par niveau cible (GDD exact)
const FORGE_FAIL_CHANCE: Record<number, number> = {
  1: 0,
  2: 0,
  3: 0.20,
  4: 0.30,
  5: 0.40,
};

@Injectable()
export class ForgeService {
  constructor(
    @InjectRepository(CharacterItem)
    private readonly charItemRepository: Repository<CharacterItem>,
    @InjectRepository(Character)
    private readonly characterRepository: Repository<Character>,
    private readonly eventEmitter: EventEmitter2,
    private readonly dataSource: DataSource,
  ) {}

  async upgradeItem(charItemId: string, user: User) {
    return this.dataSource.transaction(async (manager) => {
      // Lock le personnage
      const char = await manager
        .getRepository(Character)
        .createQueryBuilder('c')
        .setLock('pessimistic_write')
        .where('c.user_id = :userId', { userId: user.id })
        .getOne();
      if (!char) throw new BadRequestException('Aucun personnage trouvé');

      // Lock l'item
      const charItem = await manager
        .getRepository(CharacterItem)
        .createQueryBuilder('ci')
        .setLock('pessimistic_write')
        .leftJoinAndSelect('ci.item', 'item')
        .where('ci.id = :id', { id: charItemId })
        .andWhere('ci.character_id = :cid', { cid: char.id })
        .getOne();
      if (!charItem) throw new NotFoundException('Item non trouvé dans l\'inventaire');
      if (charItem.forgeLevel >= MAX_FORGE_LEVEL) {
        throw new BadRequestException(`Niveau de forge maximum atteint (${MAX_FORGE_LEVEL})`);
      }

      const targetLevel = charItem.forgeLevel + 1;
      const goldCost = FORGE_GOLD_COST[targetLevel] ?? 0;

      // Vérifier endurance
      const elapsedMinutes = (Date.now() - char.lastEnduranceTs.getTime()) / 60_000;
      const recharge = Math.floor(elapsedMinutes / 3);
      const enduranceCurrent = Math.min(char.enduranceSaved + recharge, char.enduranceMax);

      if (enduranceCurrent < FORGE_ENDURANCE_COST) {
        throw new BadRequestException(
          `Endurance insuffisante (${enduranceCurrent}/${FORGE_ENDURANCE_COST} requis)`,
        );
      }

      // Vérifier or
      if (char.gold < goldCost) {
        throw new BadRequestException(
          `Or insuffisant (${char.gold}/${goldCost} requis)`,
        );
      }

      // Déduire les coûts (même en cas d'échec)
      char.gold -= goldCost;
      char.enduranceSaved = enduranceCurrent - FORGE_ENDURANCE_COST;
      char.lastEnduranceTs = new Date();

      const failChance = FORGE_FAIL_CHANCE[targetLevel] ?? 0;
      const success = Math.random() >= failChance;

      if (success) {
        charItem.forgeLevel = targetLevel;
        await manager.save(charItem);

        this.eventEmitter.emit('achievement.check', {
          characterId: char.id, type: 'forge_upgrades', increment: 1,
        });
        this.eventEmitter.emit('community.contribute', {
          characterId: char.id, type: 'total_forge_upgrades', increment: 1,
        });
        this.eventEmitter.emit('quest.progress', {
          characterId: char.id, type: 'forge_item', increment: 1,
        });
      }

      await manager.save(char);

      const statLabel = charItem.item.type === 'weapon'
        ? `+${FORGE_BONUS_PER_LEVEL} ATK`
        : `+${FORGE_BONUS_PER_LEVEL} DEF`;

      if (success) {
        return {
          success: true,
          forgeLevel: charItem.forgeLevel,
          item: charItem.item.name,
          goldSpent: goldCost,
          message: `Forge réussie ! ${charItem.item.name} [+${charItem.forgeLevel}] (${statLabel}). -${goldCost} Or.`,
        };
      }

      return {
        success: false,
        forgeLevel: charItem.forgeLevel,
        item: charItem.item.name,
        goldSpent: goldCost,
        message: `Échec de forge ! ${charItem.item.name} reste au niveau [+${charItem.forgeLevel}]. -${goldCost} Or perdus.`,
      };
    });
  }
}
