const { EmbedBuilder } = require('discord.js');
const { checkAchievements } = require('../commands/achievements');
const { getServerMode, getEmbedColor } = require('../utils/modeHelper');
const { updateStandaloneXp, calculateLevel: standaloneCalcLevel } = require('../utils/standaloneXp');

const xpCooldowns = new Map();
const xpConfigCache = new Map();
const CACHE_TTL = 60000; // 1 minute cache

async function updateQuestProgress(supabase, userId, guildId, objective, amount = 1) {
  try {
    const now = new Date().toISOString();
    
    const { data: allQuests } = await supabase
      .from('quests')
      .select('*')
      .eq('guild_id', guildId)
      .eq('objective', objective)
      .eq('active', true);

    if (!allQuests || allQuests.length === 0) return;

    const activeQuests = allQuests.filter(quest => {
      const notExpired = !quest.expires_at || new Date(quest.expires_at) > new Date(now);
      const hasStarted = !quest.starts_at || new Date(quest.starts_at) <= new Date(now);
      return notExpired && hasStarted;
    });

    if (!activeQuests || activeQuests.length === 0) return;

    for (const quest of activeQuests) {
      const { data: userQuest } = await supabase
        .from('user_quests')
        .select('*')
        .eq('user_id', userId)
        .eq('guild_id', guildId)
        .eq('quest_id', quest.id)
        .maybeSingle();

      if (userQuest) {
        if (userQuest.claimed && !quest.repeatable) continue;
        if (userQuest.completed && !quest.repeatable) continue;

        const newProgress = (userQuest.progress || 0) + amount;
        const completed = newProgress >= quest.target_value;

        const updates = {
          progress: Math.min(newProgress, quest.target_value)
        };

        if (completed && !userQuest.completed) {
          updates.completed = true;
          updates.completed_at = now;
        }

        await supabase
          .from('user_quests')
          .update(updates)
          .eq('id', userQuest.id);
      } else {
        const progress = Math.min(amount, quest.target_value);
        const completed = progress >= quest.target_value;

        await supabase
          .from('user_quests')
          .insert({
            user_id: userId,
            guild_id: guildId,
            quest_id: quest.id,
            progress: progress,
            completed: completed,
            completed_at: completed ? now : null,
            started_at: now
          });
      }
    }
  } catch (e) {
    // Silently ignore quest tracking errors
  }
}

module.exports = {
  name: 'messageCreate',
  
  async execute(message, client, supabase) {
    if (!supabase) return;
    if (message.author.bot) return;
    if (!message.guild) return;

    const discordUserId = message.author.id;
    const guildId = message.guildId;
    const channelId = message.channelId;
    const now = Date.now();

    // Get XP config for this server
    const config = await getXpConfig(supabase, guildId);
    
    // Check if XP is enabled
    if (!config.xp_enabled) return;

    // Check cooldown
    const cooldownKey = `${guildId}:${discordUserId}`;
    const lastXp = xpCooldowns.get(cooldownKey) || 0;
    const cooldownMs = (config.message_cooldown || 60) * 1000;

    if (now - lastXp < cooldownMs) return;

    // Check server mode
    const serverMode = await getServerMode(supabase, guildId);

    // Handle standalone mode separately
    if (serverMode === 'standalone') {
      await handleStandaloneXp(message, client, supabase, config, discordUserId, guildId, channelId, now);
      return;
    }

    // FEDERATED MODE - requires linked account
    try {
      const { data: link, error: linkError } = await supabase
        .from('discord_links')
        .select('user_id')
        .eq('discord_id', discordUserId)
        .maybeSingle();

      if (linkError || !link) return;

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

      if (profileError || !profile) return;

      // Calculate base XP
      let xpGain = config.message_xp || 5;
      const prestige = profile.prestige_level || 0;

      // Apply channel bonus
      const bonusChannels = config.bonus_channels || [];
      const channelBonus = bonusChannels.find(c => c.channel_id === channelId);
      if (channelBonus) {
        xpGain = Math.floor(xpGain * channelBonus.multiplier);
      }

      // Apply role multipliers (use highest)
      const multiplierRoles = config.multiplier_roles || [];
      let highestMultiplier = 1;
      for (const mr of multiplierRoles) {
        if (message.member?.roles.cache.has(mr.role_id)) {
          highestMultiplier = Math.max(highestMultiplier, mr.multiplier);
        }
      }
      
      // Server boosters get 1.5x XP bonus automatically
      if (message.member?.premiumSince) {
        highestMultiplier = Math.max(highestMultiplier, 1.5);
      }
      
      xpGain = Math.floor(xpGain * highestMultiplier);

      // Apply prestige bonus (+5% per prestige level)
      if (prestige > 0) {
        const prestigeBonus = 1 + (prestige * 0.05);
        xpGain = Math.floor(xpGain * prestigeBonus);
      }

      const currentXp = profile.xp || 0;
      const newXp = currentXp + xpGain;
      
      // Calculate levels based on curve
      const oldLevel = calculateLevel(currentXp, config.level_curve);
      const newLevel = calculateLevel(newXp, config.level_curve);

      // Update XP and track total earned
      const totalEarned = (profile.total_xp_earned || currentXp) + xpGain;
      const { error: updateError } = await supabase
        .from('user_profiles')
        .update({ xp: newXp, total_xp_earned: totalEarned })
        .eq('id', link.user_id);

      if (updateError) return;

      xpCooldowns.set(cooldownKey, now);
      
      // Track XP for analytics
      if (client.trackXP) {
        client.trackXP(xpGain);
      }

      // Track periodic XP for weekly/monthly leaderboards (true = is a message)
      await updatePeriodicXp(supabase, link.user_id, guildId, discordUserId, xpGain, true);

      // Update user stats for achievements
      await updateUserStats(supabase, link.user_id, guildId, { messages: 1 });

      // Get updated stats for achievement checking
      const stats = await getUserStats(supabase, link.user_id, guildId);
      stats.level = newLevel;
      stats.prestige = prestige;
      stats.totalXp = totalEarned;

      // Get daily streak from profile
      const { data: fullProfile } = await supabase
        .from('user_profiles')
        .select('daily_streak')
        .eq('id', link.user_id)
        .maybeSingle();
      stats.dailyStreak = fullProfile?.daily_streak || 0;

      if (newLevel > oldLevel) {
        await sendLevelUpAnnouncement(message, newLevel, newXp, config, client);
        await checkMilestoneRoles(message.member, {
          level: newLevel,
          prestige: prestige,
          totalXp: totalEarned
        }, supabase, guildId);
      } else {
        await checkMilestoneRoles(message.member, {
          level: newLevel,
          prestige: prestige,
          totalXp: totalEarned
        }, supabase, guildId, true);
      }

      // Check for achievements
      await checkAchievements(link.user_id, message.member, stats, supabase, guildId, client);

      // Track quest progress for messages and XP earned
      await updateQuestProgress(supabase, link.user_id, guildId, 'messages', 1);
      await updateQuestProgress(supabase, link.user_id, guildId, 'xp_earned', xpGain);

      // Track level-up quest progress
      if (newLevel > oldLevel) {
        await updateQuestProgress(supabase, link.user_id, guildId, 'level_ups', 1);
      }

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

async function sendLevelUpAnnouncement(message, newLevel, newXp, config, client) {
  try {
    const messageTemplate = config.levelup_message || '🎉 Congratulations {user}! You reached **Level {level}**!';
    const channelId = config.levelup_channel_id;
    const sendDm = config.levelup_dm === true;
    const useEmbed = config.levelup_embed === true;
    const embedColor = config.levelup_embed_color || '#5865F2';

    const formattedMessage = messageTemplate
      .replace(/{user}/g, message.author.toString())
      .replace(/{username}/g, message.author.username)
      .replace(/{level}/g, newLevel.toString())
      .replace(/{xp}/g, newXp.toLocaleString())
      .replace(/{server}/g, message.guild.name);

    let messageContent;
    
    if (useEmbed) {
      const embed = new EmbedBuilder()
        .setDescription(formattedMessage)
        .setColor(parseInt(embedColor.replace('#', ''), 16))
        .setThumbnail(message.author.displayAvatarURL({ dynamic: true }))
        .setTimestamp();
      
      messageContent = { embeds: [embed] };
    } else {
      messageContent = { content: formattedMessage };
    }

    if (sendDm) {
      const dmSent = await message.author.send(messageContent).catch(() => null);
      if (!dmSent) {
        await message.channel.send(messageContent).catch(() => {});
      }
    } else if (channelId) {
      const channel = await client.channels.fetch(channelId).catch(() => null);
      if (channel) {
        await channel.send(messageContent).catch(() => {});
      } else {
        await message.channel.send(messageContent).catch(() => {});
      }
    } else {
      await message.channel.send(messageContent).catch(() => {});
    }
  } catch (error) {
    console.error('Level-up announcement error:', error.message);
  }
}

async function getXpConfig(supabase, guildId) {
  const now = Date.now();
  const cached = xpConfigCache.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) {
      // Table might not exist - return defaults
      const defaults = getDefaultConfig();
      xpConfigCache.set(guildId, { config: defaults, timestamp: now });
      return defaults;
    }

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

function getDefaultConfig() {
  return {
    message_xp: 5,
    message_cooldown: 60,
    multiplier_roles: [],
    bonus_channels: [],
    level_curve: 'normal',
    xp_enabled: true
  };
}

function calculateLevel(xp, curve = 'normal') {
  // XP required per level: level² * base
  // So level = sqrt(xp / base)
  const bases = {
    easy: 50,    // Faster leveling
    normal: 100, // Standard
    hard: 200    // Slower leveling
  };
  const base = bases[curve] || 100;
  return Math.floor(Math.sqrt(xp / base));
}

async function checkMilestoneRoles(member, milestones, supabase, guildId, xpOnly = false) {
  if (!member || !supabase) return;

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

    if (error || !allRoles || allRoles.length === 0) return;

    const rolesToAdd = [];
    const rolesToRemove = [];

    for (const roleConfig of allRoles) {
      const { role_id, milestone_type, milestone_value, stack_roles } = roleConfig;
      let qualifies = false;

      switch (milestone_type) {
        case 'level':
          if (!xpOnly) qualifies = milestones.level >= milestone_value;
          break;
        case 'prestige':
          if (!xpOnly) qualifies = milestones.prestige >= milestone_value;
          break;
        case 'total_xp':
          qualifies = milestones.totalXp >= milestone_value;
          break;
      }

      if (qualifies && !member.roles.cache.has(role_id)) {
        rolesToAdd.push({ role_id, milestone_type, milestone_value, stack_roles });
      }
    }

    for (const roleToAdd of rolesToAdd) {
      try {
        await member.roles.add(roleToAdd.role_id);
        
        if (!roleToAdd.stack_roles) {
          const sameTypeRoles = allRoles.filter(r => 
            r.milestone_type === roleToAdd.milestone_type && 
            r.milestone_value < roleToAdd.milestone_value &&
            member.roles.cache.has(r.role_id)
          );
          
          for (const oldRole of sameTypeRoles) {
            if (!rolesToRemove.includes(oldRole.role_id)) {
              rolesToRemove.push(oldRole.role_id);
            }
          }
        }
      } catch (e) {
        // Role addition failed, don't remove old roles
      }
    }

    for (const roleId of rolesToRemove) {
      await member.roles.remove(roleId).catch(() => {});
    }
  } catch (e) {
    // Table may not exist yet - silently ignore
  }
}

async function updateUserStats(supabase, userId, guildId, increments) {
  try {
    const { data: existing } = await supabase
      .from('user_stats')
      .select('*')
      .eq('user_id', userId)
      .eq('guild_id', guildId)
      .maybeSingle();

    if (existing) {
      const updates = { updated_at: new Date().toISOString() };
      if (increments.messages) updates.messages = (existing.messages || 0) + increments.messages;
      if (increments.reactionsGiven) updates.reactions_given = (existing.reactions_given || 0) + increments.reactionsGiven;
      if (increments.reactionsReceived) updates.reactions_received = (existing.reactions_received || 0) + increments.reactionsReceived;
      if (increments.voiceMinutes) updates.voice_minutes = (existing.voice_minutes || 0) + increments.voiceMinutes;
      if (increments.commandsUsed) updates.commands_used = (existing.commands_used || 0) + increments.commandsUsed;

      await supabase
        .from('user_stats')
        .update(updates)
        .eq('user_id', userId)
        .eq('guild_id', guildId);
    } else {
      await supabase
        .from('user_stats')
        .insert({
          user_id: userId,
          guild_id: guildId,
          messages: increments.messages || 0,
          reactions_given: increments.reactionsGiven || 0,
          reactions_received: increments.reactionsReceived || 0,
          voice_minutes: increments.voiceMinutes || 0,
          commands_used: increments.commandsUsed || 0
        });
    }
  } catch (e) {
    // Table may not exist - silently ignore
  }
}

async function getUserStats(supabase, userId, guildId) {
  try {
    const { data } = await supabase
      .from('user_stats')
      .select('*')
      .eq('user_id', userId)
      .eq('guild_id', guildId)
      .maybeSingle();

    return {
      messages: data?.messages || 0,
      reactionsGiven: data?.reactions_given || 0,
      reactionsReceived: data?.reactions_received || 0,
      voiceMinutes: data?.voice_minutes || 0,
      commandsUsed: data?.commands_used || 0
    };
  } catch (e) {
    return { messages: 0, reactionsGiven: 0, reactionsReceived: 0, voiceMinutes: 0, commandsUsed: 0 };
  }
}

async function updatePeriodicXp(supabase, userId, guildId, discordId, xpGain, isMessage = false) {
  try {
    const now = new Date();
    
    // Calculate week start (Monday)
    const dayOfWeek = now.getDay();
    const diffToMonday = dayOfWeek === 0 ? 6 : dayOfWeek - 1;
    const weekStart = new Date(now);
    weekStart.setDate(now.getDate() - diffToMonday);
    weekStart.setHours(0, 0, 0, 0);
    const weekStartStr = weekStart.toISOString().split('T')[0];

    // Calculate month start
    const monthStart = new Date(now.getFullYear(), now.getMonth(), 1);
    const monthStartStr = monthStart.toISOString().split('T')[0];

    // Update weekly record (period_type = 'week', period_start = week_start)
    await upsertPeriodXp(supabase, userId, guildId, discordId, 'week', weekStartStr, xpGain, isMessage);
    
    // Update monthly record (period_type = 'month', period_start = month_start)
    await upsertPeriodXp(supabase, userId, guildId, discordId, 'month', monthStartStr, xpGain, isMessage);
    
  } catch (e) {
    // Table may not exist - silently ignore
  }
}

async function upsertPeriodXp(supabase, userId, guildId, discordId, periodType, periodStart, xpGain, isMessage) {
  try {
    // Find record by (user, guild, period_type, period_start)
    const { data: existing } = await supabase
      .from('periodic_xp')
      .select('*')
      .eq('user_id', userId)
      .eq('guild_id', guildId)
      .eq('period_type', periodType)
      .eq('period_start', periodStart)
      .maybeSingle();

    if (existing) {
      const xpField = periodType === 'week' ? 'weekly_xp' : 'monthly_xp';
      const msgField = periodType === 'week' ? 'weekly_messages' : 'monthly_messages';
      
      const updates = {
        [xpField]: (existing[xpField] || 0) + xpGain,
        updated_at: new Date().toISOString()
      };
      
      if (isMessage) {
        updates[msgField] = (existing[msgField] || 0) + 1;
      }
      
      await supabase
        .from('periodic_xp')
        .update(updates)
        .eq('id', existing.id);
    } else {
      const insertData = {
        user_id: userId,
        guild_id: guildId,
        discord_id: discordId,
        period_type: periodType,
        period_start: periodStart,
        week_start: periodType === 'week' ? periodStart : null,
        month_start: periodType === 'month' ? periodStart : null,
        weekly_xp: periodType === 'week' ? xpGain : 0,
        monthly_xp: periodType === 'month' ? xpGain : 0,
        weekly_messages: (periodType === 'week' && isMessage) ? 1 : 0,
        monthly_messages: (periodType === 'month' && isMessage) ? 1 : 0
      };
      
      await supabase
        .from('periodic_xp')
        .insert(insertData);
    }
  } catch (e) {
    // Silently ignore
  }
}

// STANDALONE MODE XP HANDLER
async function handleStandaloneXp(message, client, supabase, config, discordUserId, guildId, channelId, now) {
  try {
    // Get existing standalone XP data
    const { data: existingXp } = await supabase
      .from('guild_user_xp')
      .select('*')
      .eq('discord_id', discordUserId)
      .eq('guild_id', guildId)
      .maybeSingle();

    // Calculate base XP
    let xpGain = config.message_xp || 5;
    const prestige = existingXp?.prestige_level || 0;

    // Apply channel bonus
    const bonusChannels = config.bonus_channels || [];
    const channelBonus = bonusChannels.find(c => c.channel_id === channelId);
    if (channelBonus) {
      xpGain = Math.floor(xpGain * channelBonus.multiplier);
    }

    // Apply role multipliers (use highest)
    const multiplierRoles = config.multiplier_roles || [];
    let highestMultiplier = 1;
    for (const mr of multiplierRoles) {
      if (message.member?.roles.cache.has(mr.role_id)) {
        highestMultiplier = Math.max(highestMultiplier, mr.multiplier);
      }
    }
    
    // Server boosters get 1.5x XP bonus automatically
    if (message.member?.premiumSince) {
      highestMultiplier = Math.max(highestMultiplier, 1.5);
    }
    
    xpGain = Math.floor(xpGain * highestMultiplier);

    // Apply prestige bonus (+5% per prestige level)
    if (prestige > 0) {
      const prestigeBonus = 1 + (prestige * 0.05);
      xpGain = Math.floor(xpGain * prestigeBonus);
    }

    const currentXp = existingXp?.xp || 0;
    const newXp = currentXp + xpGain;
    
    // Calculate levels
    const oldLevel = standaloneCalcLevel(currentXp, config.level_curve);
    const newLevel = standaloneCalcLevel(newXp, config.level_curve);

    // Update or create standalone XP record
    const result = await updateStandaloneXp(supabase, discordUserId, guildId, xpGain, message.author.username);
    
    if (!result) return;

    // Update cooldown
    const cooldownKey = `${guildId}:${discordUserId}`;
    xpCooldowns.set(cooldownKey, now);
    
    // Track XP for analytics
    if (client.trackXP) {
      client.trackXP(xpGain);
    }

    // Level up announcement for standalone
    if (newLevel > oldLevel) {
      await sendStandaloneLevelUp(message, newLevel, newXp, config, client);
      await checkMilestoneRolesStandalone(message.member, {
        level: newLevel,
        prestige: prestige,
        totalXp: result.total_xp_earned || newXp
      }, supabase, guildId);
    }

  } catch (error) {
    console.error('Standalone XP tracking error:', error.message);
  }
}

async function sendStandaloneLevelUp(message, newLevel, newXp, config, client) {
  try {
    const messageTemplate = config.levelup_message || '🎉 Congratulations {user}! You reached **Level {level}**!';
    const channelId = config.levelup_channel_id;
    const sendDm = config.levelup_dm === true;
    const useEmbed = config.levelup_embed === true;
    const embedColor = '#6B7280'; // Standalone gray color

    const formattedMessage = messageTemplate
      .replace(/{user}/g, message.author.toString())
      .replace(/{username}/g, message.author.username)
      .replace(/{level}/g, newLevel.toString())
      .replace(/{xp}/g, newXp.toLocaleString())
      .replace(/{server}/g, message.guild.name);

    let messageContent;
    
    if (useEmbed) {
      const embed = new EmbedBuilder()
        .setDescription(formattedMessage)
        .setColor(parseInt(embedColor.replace('#', ''), 16))
        .setThumbnail(message.author.displayAvatarURL({ dynamic: true }))
        .setFooter({ text: '🏠 Standalone Mode' })
        .setTimestamp();
      
      messageContent = { embeds: [embed] };
    } else {
      messageContent = { content: formattedMessage };
    }

    if (sendDm) {
      const dmSent = await message.author.send(messageContent).catch(() => null);
      if (!dmSent) {
        await message.channel.send(messageContent).catch(() => {});
      }
    } else if (channelId) {
      const channel = await client.channels.fetch(channelId).catch(() => null);
      if (channel) {
        await channel.send(messageContent).catch(() => {});
      } else {
        await message.channel.send(messageContent).catch(() => {});
      }
    } else {
      await message.channel.send(messageContent).catch(() => {});
    }
  } catch (error) {
    console.error('Standalone level-up announcement error:', error.message);
  }
}

async function checkMilestoneRolesStandalone(member, milestones, supabase, guildId) {
  if (!member || !supabase) return;

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

    if (error || !allRoles || allRoles.length === 0) return;

    const rolesToAdd = [];
    const rolesToRemove = [];

    for (const roleConfig of allRoles) {
      const { role_id, milestone_type, milestone_value, stack_roles } = roleConfig;
      let qualifies = false;

      switch (milestone_type) {
        case 'level':
          qualifies = milestones.level >= milestone_value;
          break;
        case 'prestige':
          qualifies = milestones.prestige >= milestone_value;
          break;
        case 'total_xp':
          qualifies = milestones.totalXp >= milestone_value;
          break;
      }

      if (qualifies && !member.roles.cache.has(role_id)) {
        rolesToAdd.push({ role_id, milestone_type, milestone_value, stack_roles });
      }
    }

    for (const roleToAdd of rolesToAdd) {
      try {
        await member.roles.add(roleToAdd.role_id);
        
        if (!roleToAdd.stack_roles) {
          const sameTypeRoles = allRoles.filter(r => 
            r.milestone_type === roleToAdd.milestone_type && 
            r.milestone_value < roleToAdd.milestone_value &&
            member.roles.cache.has(r.role_id)
          );
          
          for (const oldRole of sameTypeRoles) {
            if (!rolesToRemove.includes(oldRole.role_id)) {
              rolesToRemove.push(oldRole.role_id);
            }
          }
        }
      } catch (e) {
        // Role addition failed
      }
    }

    for (const roleId of rolesToRemove) {
      await member.roles.remove(roleId).catch(() => {});
    }
  } catch (e) {
    // Silently ignore
  }
}

// Export functions for use in other commands
module.exports.calculateLevel = calculateLevel;
module.exports.checkMilestoneRoles = checkMilestoneRoles;
module.exports.updateUserStats = updateUserStats;
module.exports.getUserStats = getUserStats;
module.exports.updatePeriodicXp = updatePeriodicXp;
module.exports.updateQuestProgress = updateQuestProgress;
