const { checkMilestoneRoles, calculateLevel, updateUserStats, getUserStats, updatePeriodicXp, updateQuestProgress } = require('./xpTracker');
const { checkAchievements } = require('../commands/achievements');

const voiceSessions = new Map();
const voiceConfigCache = new Map();
const CACHE_TTL = 60000;
const XP_INTERVAL = 60000;

let xpInterval = null;

module.exports = {
  name: 'voiceStateUpdate',
  
  async execute(oldState, newState, client, supabase) {
    if (!supabase) return;
    
    const userId = newState.member?.id || oldState.member?.id;
    if (!userId) return;
    
    const member = newState.member || oldState.member;
    if (member?.user?.bot) return;
    
    const guildId = newState.guild?.id || oldState.guild?.id;
    if (!guildId) return;

    const wasInVoice = oldState.channelId !== null;
    const isInVoice = newState.channelId !== null;
    const now = Date.now();
    const key = `${guildId}:${userId}`;

    if (!wasInVoice && isInVoice) {
      const config = await getVoiceConfig(supabase, guildId);
      if (!config.voice_xp_enabled) return;
      
      if (isAfkChannel(newState.channelId, newState.guild)) return;
      
      voiceSessions.set(key, {
        joinTime: now,
        lastAwardTime: now,
        guildId,
        userId
      });
      
      startXpInterval(client, supabase);
    }
    else if (wasInVoice && !isInVoice) {
      const session = voiceSessions.get(key);
      
      if (session) {
        await awardRemainingXp(supabase, client, session, member, now);
        voiceSessions.delete(key);
      }
    }
    else if (wasInVoice && isInVoice && oldState.channelId !== newState.channelId) {
      const session = voiceSessions.get(key);
      
      if (isAfkChannel(newState.channelId, newState.guild)) {
        if (session) {
          await awardRemainingXp(supabase, client, session, member, now);
          voiceSessions.delete(key);
        }
      } else if (isAfkChannel(oldState.channelId, oldState.guild)) {
        const config = await getVoiceConfig(supabase, guildId);
        if (config.voice_xp_enabled) {
          voiceSessions.set(key, {
            joinTime: now,
            lastAwardTime: now,
            guildId,
            userId
          });
        }
      }
    }
  }
};

function startXpInterval(client, supabase) {
  if (xpInterval) return;
  
  xpInterval = setInterval(async () => {
    if (voiceSessions.size === 0) {
      clearInterval(xpInterval);
      xpInterval = null;
      return;
    }
    
    const now = Date.now();
    
    for (const [key, session] of voiceSessions.entries()) {
      const { guildId, userId, lastAwardTime } = session;
      
      try {
        const guild = client.guilds.cache.get(guildId);
        if (!guild) continue;
        
        const member = await guild.members.fetch(userId).catch(() => null);
        if (!member) continue;
        
        const voiceState = member.voice;
        if (!voiceState?.channelId) {
          voiceSessions.delete(key);
          continue;
        }
        
        if (isAfkChannel(voiceState.channelId, guild)) continue;
        
        const config = await getVoiceConfig(supabase, guildId);
        if (!config.voice_xp_enabled) continue;
        
        const cooldownMs = (config.voice_cooldown || 60) * 1000;
        const timeSinceLastAward = now - lastAwardTime;
        
        if (timeSinceLastAward < cooldownMs) continue;
        
        const minutesToAward = Math.floor(timeSinceLastAward / 60000);
        if (minutesToAward < 1) continue;
        
        await grantVoiceXp(supabase, client, guildId, userId, config, member, minutesToAward);
        
        session.lastAwardTime += minutesToAward * 60000;
        
      } catch (error) {
        console.error('Voice XP interval error:', error.message);
      }
    }
  }, XP_INTERVAL);
}

async function awardRemainingXp(supabase, client, session, member, now) {
  const { guildId, userId, lastAwardTime } = session;
  
  const config = await getVoiceConfig(supabase, guildId);
  if (!config.voice_xp_enabled) return;
  
  const timeSinceLastAward = now - lastAwardTime;
  const minutesToAward = Math.floor(timeSinceLastAward / 60000);
  
  if (minutesToAward < 1) return;
  
  await grantVoiceXp(supabase, client, guildId, userId, config, member, minutesToAward);
}

async function grantVoiceXp(supabase, client, guildId, userId, config, member, minutes = 1) {
  try {
    const { data: link, error: linkError } = await supabase
      .from('discord_links')
      .select('user_id')
      .eq('discord_id', userId)
      .maybeSingle();

    if (linkError || !link) return;

    const { data: profile, error: profileError } = await supabase
      .from('user_profiles')
      .select('xp, prestige_level, total_xp_earned, daily_streak')
      .eq('id', link.user_id)
      .maybeSingle();

    if (profileError || !profile) return;

    let xpGain = (config.voice_xp || 2) * minutes;
    const prestige = profile.prestige_level || 0;

    const multiplierRoles = config.multiplier_roles || [];
    let highestMultiplier = 1;
    for (const mr of multiplierRoles) {
      if (member?.roles?.cache?.has(mr.role_id)) {
        highestMultiplier = Math.max(highestMultiplier, mr.multiplier);
      }
    }
    
    if (member?.premiumSince) {
      highestMultiplier = Math.max(highestMultiplier, 1.5);
    }
    
    xpGain = Math.floor(xpGain * highestMultiplier);

    if (prestige > 0) {
      const prestigeBonus = 1 + (prestige * 0.05);
      xpGain = Math.floor(xpGain * prestigeBonus);
    }

    const currentXp = profile.xp || 0;
    const newXp = currentXp + xpGain;
    const totalEarned = (profile.total_xp_earned || currentXp) + xpGain;
    
    const oldLevel = calculateLevel(currentXp, config.level_curve);
    const newLevel = calculateLevel(newXp, config.level_curve);

    const { error: updateError } = await supabase
      .from('user_profiles')
      .update({ xp: newXp, total_xp_earned: totalEarned })
      .eq('id', link.user_id);

    if (updateError) return;

    if (client.trackXP) {
      client.trackXP(xpGain);
    }

    // Track periodic XP for weekly/monthly leaderboards (false = not a message)
    await updatePeriodicXp(supabase, link.user_id, guildId, userId, xpGain, false);

    // Track voice minutes for achievements
    await updateUserStats(supabase, link.user_id, guildId, { voiceMinutes: minutes });

    if (member) {
      if (newLevel > oldLevel) {
        const serverConfig = client.serverConfigs?.get(guildId);
        const levelUpChannelId = serverConfig?.level_up_channel;

        const levelUpMessage = `🎉 Congratulations ${member}! You reached **Level ${newLevel}** from voice chat activity!`;

        if (levelUpChannelId) {
          const channel = await client.channels.fetch(levelUpChannelId).catch(() => null);
          if (channel) {
            await channel.send(levelUpMessage);
          }
        }
      }

      await checkMilestoneRoles(member, {
        level: newLevel,
        prestige: prestige,
        totalXp: totalEarned
      }, supabase, guildId, newLevel <= oldLevel);

      // Get updated stats and check achievements
      const stats = await getUserStats(supabase, link.user_id, guildId);
      stats.level = newLevel;
      stats.prestige = prestige;
      stats.totalXp = totalEarned;
      stats.dailyStreak = profile.daily_streak || 0;

      await checkAchievements(link.user_id, member, stats, supabase, guildId, client);

      // Track quest progress for voice chat
      await updateQuestProgress(supabase, link.user_id, guildId, 'voice_minutes', minutes);
      await updateQuestProgress(supabase, link.user_id, guildId, 'xp_earned', xpGain);

      if (newLevel > oldLevel) {
        await updateQuestProgress(supabase, link.user_id, guildId, 'level_ups', 1);
      }
    }

  } catch (error) {
    console.error('Voice XP grant error:', error.message);
  }
}

function isAfkChannel(channelId, guild) {
  return guild?.afkChannelId === channelId;
}

async function getVoiceConfig(supabase, guildId) {
  const now = Date.now();
  const cached = voiceConfigCache.get(guildId);
  
  if (cached && (now - cached.timestamp < CACHE_TTL)) {
    return cached.config;
  }

  try {
    const { data, error } = await supabase
      .from('xp_config')
      .select('*')
      .eq('guild_id', guildId)
      .maybeSingle();

    if (error) {
      const defaults = getDefaultConfig();
      voiceConfigCache.set(guildId, { config: defaults, timestamp: now });
      return defaults;
    }

    const config = data || getDefaultConfig();
    voiceConfigCache.set(guildId, { config, timestamp: now });
    return config;
  } catch (e) {
    return getDefaultConfig();
  }
}

function getDefaultConfig() {
  return {
    voice_xp: 2,
    voice_cooldown: 60,
    voice_xp_enabled: true,
    multiplier_roles: [],
    level_curve: 'normal'
  };
}

