import { prisma } from '../database';
import { logger } from '../utils/logger';
import { FeverService } from './FeverService';
import { RefinementLevelConfig, RefinementBattleConfig, RefinementSystemConfig } from '@prisma/client';

export interface RefineResult {
  success: boolean;
  destroyed: boolean;
  levelBefore: number;
  levelAfter: number;
  cost: number;
  remainingGold: number;
}

export interface BattleResult {
  winnerId: string;
  loserId: string;
  attackerId: string;
  targetId: string;
  reward: number;
  attackerLevel: number;
  targetLevel: number;
  attackerDurability: number;
  targetDurability: number;
  destroyed: boolean; // 공격자 무기 파괴 여부 (내구도 0에서 공격 시)
}

export class RefinementService {
  // 캐시 필드
  private static levelConfigs = new Map<number, RefinementLevelConfig>();
  private static battleConfigs = new Map<number, number>(); // Gap -> WinRate
  private static systemConfigs = new Map<string, string>(); // Key -> Value
  private static isInitialized = false;

  /**
   * 레벨에 따른 최대 내구도 계산 (10 + level)
   */
  public static getMaxDurability(level: number): number {
    return 10 + level;
  }

  /**
   * DB에서 밸런스 설정을 로드하여 메모리에 캐싱
   */
  public static async loadConfigs(force: boolean = false) {
    if (this.isInitialized && !force) return;

    const [levels, battles, systems] = await Promise.all([
      prisma.refinementLevelConfig.findMany(),
      prisma.refinementBattleConfig.findMany(),
      prisma.refinementSystemConfig.findMany(),
    ]);

    this.levelConfigs.clear();
    levels.forEach((c: RefinementLevelConfig) => this.levelConfigs.set(c.level, c));

    this.battleConfigs.clear();
    battles.forEach((c: RefinementBattleConfig) => this.battleConfigs.set(c.levelGap, c.winRate));

    this.systemConfigs.clear();
    systems.forEach((c: RefinementSystemConfig) => this.systemConfigs.set(c.key, c.value));

    this.isInitialized = true;
    logger.info(`[Refinement] Loaded ${levels.length} levels, ${battles.length} battle gaps, and ${systems.length} system configs.`);
  }

  private static getSysConfig(key: string, defaultValue: string): string {
    return this.systemConfigs.get(key) ?? defaultValue;
  }

  private static getSysConfigNum(key: string, defaultValue: number): number {
    const val = this.systemConfigs.get(key);
    return val ? Number.parseFloat(val) : defaultValue;
  }
  
  /**
   * 레벨에 따른 강화 비용 가져오기
   */
  public static getCost(level: number): number {
    return this.levelConfigs.get(level)?.cost ?? 999999999;
  }

  /**
   * 재련 시도
   */
  public static async tryRefine(userId: string, guildId: string): Promise<RefineResult> {
    await this.loadConfigs();
    const profile = await this.getOrCreateProfile(userId, guildId);
    
    const maxLevel = this.getSysConfigNum('MAX_LEVEL', 25);
    if (profile.weaponLevel >= maxLevel) {
      throw new Error(`이미 최대 단계(${maxLevel}강)에 도달했습니다. 무기를 판매하고 다시 시작하세요!`);
    }

    const levelConfig = this.levelConfigs.get(profile.weaponLevel);
    if (!levelConfig) throw new Error('강화 정보를 불러올 수 없습니다.');

    const cost = levelConfig.cost;

    if (profile.gold < cost) {
      throw new Error('골드가 부족합니다.');
    }

    const fever = await FeverService.getFeverBonus(guildId);
    const successRate = levelConfig.successRate + (fever.active ? fever.bonusRate : 0);
    
    const random = Math.random();
    const success = random <= successRate;
    let destroyed = false;
    let newLevel = profile.weaponLevel;
    let newDurability = profile.durability;

    if (success) {
      newLevel = Math.min(maxLevel, profile.weaponLevel + 1);
      newDurability = this.getMaxDurability(newLevel);
    } else {
      const destroyRate = levelConfig.destroyRate;
      if (Math.random() <= destroyRate) {
        destroyed = true;
        newLevel = 0;
        newDurability = 10;
      }
    }

    const updatedProfile = await prisma.refinementProfile.update({
      where: { userId_guildId: { userId, guildId } },
      data: {
        gold: { decrement: cost },
        weaponLevel: newLevel,
        durability: newDurability,
        isDisabled: newDurability > 0 ? false : undefined,
        maxWeaponLevel: { set: Math.max(profile.maxWeaponLevel, newLevel) },
        tryCount: { increment: 1 },
        successCount: success ? { increment: 1 } : undefined,
        failCount: !success ? { increment: 1 } : undefined,
        destroyCount: destroyed ? { increment: 1 } : undefined,
      }
    });

    return {
      success,
      destroyed,
      levelBefore: profile.weaponLevel,
      levelAfter: newLevel,
      cost,
      remainingGold: updatedProfile.gold,
    };
  }

  /**
   * 전투 수행 (일방적)
   */
  public static async startBattle(attackerId: string, targetId: string, guildId: string): Promise<BattleResult> {
    await this.loadConfigs();
    const attacker = await this.getOrCreateProfile(attackerId, guildId);
    const target = await this.getOrCreateProfile(targetId, guildId);

    // 0. 일일 공격 횟수 체크 및 리셋 (KST 기준 자정 리셋)
    const now = new Date();
    const lastReset = attacker.lastBattleReset || new Date(0);
    const isNewDay = now.toDateString() !== lastReset.toDateString();

    const dailyLimit = this.getSysConfigNum('DAILY_BATTLE_LIMIT', 10);
    let currentDailyCount = isNewDay ? 0 : attacker.dailyBattleCount;
    if (currentDailyCount >= dailyLimit) {
      throw new Error(`일일 공격 제한(${dailyLimit}회)에 도달했습니다. 내일 다시 도전하세요!`);
    }

    if (attacker.weaponLevel === 0 || target.weaponLevel === 0) {
      throw new Error('0강 무기 소유자는 전투에 참여할 수 없습니다. 최소 1강 이상 강화하세요.');
    }

    if (attacker.isDisabled) throw new Error('현재 전투 불능 상태입니다. 무기 수리(재련 시도 등)가 필요합니다.');
    
    // 1. 승패 확률 가져오기
    const diff = attacker.weaponLevel - target.weaponLevel;
    const winRate = this.battleConfigs.get(diff) ?? (diff > 0 ? 0.99 : 0);

    const isAttackerWin = Math.random() < winRate;

    const winnerId = isAttackerWin ? attackerId : targetId;
    const loserId = isAttackerWin ? targetId : attackerId;

    // 보상 계산: 승리자 역배율 적용 (TargetCost × TargetLevel/WinnerLevel × Multiplier)
    const levelDiff = Math.abs(attacker.weaponLevel - target.weaponLevel);
    const winnerLevel = isAttackerWin ? attacker.weaponLevel : target.weaponLevel;
    const loserLevel = isAttackerWin ? target.weaponLevel : attacker.weaponLevel;
    const loserCost = this.getCost(loserLevel);
    
    const inverseMultiplier = this.getSysConfigNum('INVERSE_REWARD_MULTIPLIER', 2.0);
    const randomMin = this.getSysConfigNum('REWARD_RANDOM_MIN', 0.8);
    const randomMax = this.getSysConfigNum('REWARD_RANDOM_MAX', 1.2);

    const ratio = Math.min(1.0, loserLevel / winnerLevel);
    const baseReward = Math.floor(loserCost * ratio * inverseMultiplier);
    
    const randomFactor = randomMin + Math.random() * (randomMax - randomMin);
    const reward = Math.floor(baseReward * randomFactor) + (levelDiff * 100);

    // 내구도 감소 (양쪽 모두 -1)
    const newAttackerDurability = Math.max(0, attacker.durability - 1);
    const newTargetDurability = Math.max(0, target.durability - 1);

    // 무기 파괴/불능 처리
    const attackerDestroyed = false;
    
    await prisma.$transaction([
      prisma.refinementProfile.update({
        where: { userId_guildId: { userId: attackerId, guildId } },
        data: {
          gold: attacker.gold + (isAttackerWin ? reward : 0),
          durability: newAttackerDurability,
          isDisabled: newAttackerDurability <= 0,
          battleWin: isAttackerWin ? attacker.battleWin + 1 : attacker.battleWin,
          battleLoss: isAttackerWin ? attacker.battleLoss : attacker.battleLoss + 1,
          dailyBattleCount: currentDailyCount + 1,
          lastBattleReset: now,
        }
      }),
      prisma.refinementProfile.update({
        where: { userId_guildId: { userId: targetId, guildId } },
        data: {
          gold: target.gold + (!isAttackerWin ? reward : 0),
          durability: newTargetDurability,
          isDisabled: newTargetDurability <= 0,
          battleWin: !isAttackerWin ? target.battleWin + 1 : target.battleWin,
          battleLoss: !isAttackerWin ? target.battleLoss : target.battleLoss + 1,
        }
      })
    ]);

    return {
      winnerId,
      loserId,
      attackerId,
      targetId,
      reward,
      attackerLevel: attacker.weaponLevel,
      targetLevel: target.weaponLevel,
      attackerDurability: newAttackerDurability,
      targetDurability: newTargetDurability,
      destroyed: attackerDestroyed,
    };
  }

  /**
   * 일일 출석
   */
  public static async checkIn(userId: string, guildId: string): Promise<{ goldAdded: number; totalGold: number }> {
    const profile = await this.getOrCreateProfile(userId, guildId);
    
    const now = new Date();
    if (profile.lastCheckIn) {
      const last = new Date(profile.lastCheckIn);
      if (last.getUTCFullYear() === now.getUTCFullYear() &&
          last.getUTCMonth() === now.getUTCMonth() &&
          last.getUTCDate() === now.getUTCDate()) {
        throw new Error('오늘은 이미 출석했습니다.');
      }
    }

    const checkInGold = this.getSysConfigNum('CHECKIN_GOLD', 500);
    const updated = await prisma.refinementProfile.update({
      where: { userId_guildId: { userId, guildId } },
      data: {
        gold: { increment: checkInGold },
        lastCheckIn: now,
      }
    });

    return { goldAdded: checkInGold, totalGold: updated.gold };
  }

  /**
   * 무기 판매
   */
  public static async sellWeapon(userId: string, guildId: string): Promise<{ level: number; price: number; gold: number }> {
    await this.loadConfigs();
    const profile = await this.getOrCreateProfile(userId, guildId);
    const level = profile.weaponLevel;
    const cost = this.getCost(level);
    
    const multiplier = this.levelConfigs.get(level)?.sellMultiplier ?? 1;
    const price = Math.floor(cost * multiplier);
    
    const updated = await prisma.refinementProfile.update({
      where: { userId_guildId: { userId, guildId } },
      data: {
        gold: { increment: price },
        weaponLevel: 0,
        durability: 10, // 판매 후 초기화 시 내구도 복구
        isDisabled: false,
      }
    });

    return { level: profile.weaponLevel, price, gold: updated.gold };
  }

  public static async getProfile(userId: string, guildId: string) {
    return this.getOrCreateProfile(userId, guildId);
  }

  public static async addGold(userId: string, guildId: string, amount: number): Promise<number> {
    const profile = await this.getOrCreateProfile(userId, guildId);
    const updated = await prisma.refinementProfile.update({
      where: { userId_guildId: { userId, guildId } },
      data: {
        gold: profile.gold + amount,
      },
    });

    return updated.gold;
  }

  private static async getOrCreateProfile(userId: string, guildId: string) {
    let profile = await prisma.refinementProfile.findUnique({
      where: { userId_guildId: { userId, guildId } }
    });

    if (!profile) {
      await this.loadConfigs();
      const startGold = this.getSysConfigNum('START_GOLD', 1000);
      profile = await prisma.refinementProfile.create({
        data: { userId, guildId, gold: startGold }
      });
    }

    return profile;
  }
}
