const express = require('express');
const session = require('express-session');
const cookieParser = require('cookie-parser');
const cors = require('cors');
const path = require('path');
const crypto = require('crypto');

function createWebServer(discordClient, supabase, options = {}) {
  const app = express();
  
  const DISCORD_CLIENT_ID = process.env.DISCORD_CLIENT_ID;
  const DISCORD_CLIENT_SECRET = process.env.DISCORD_CLIENT_SECRET;
  const SESSION_SECRET = process.env.SESSION_SECRET || crypto.randomBytes(32).toString('hex');
  const BASE_URL = process.env.REPLIT_DEV_DOMAIN 
    ? `https://${process.env.REPLIT_DEV_DOMAIN}`
    : process.env.BASE_URL || 'http://localhost:5000';
  
  app.use(cors({
    origin: true,
    credentials: true
  }));
  app.use(cookieParser());
  app.use(express.json());
  app.use(express.urlencoded({ extended: true }));
  
  app.use(session({
    secret: SESSION_SECRET,
    resave: false,
    saveUninitialized: false,
    cookie: {
      secure: process.env.NODE_ENV === 'production',
      httpOnly: true,
      maxAge: 7 * 24 * 60 * 60 * 1000
    }
  }));
  
  app.use((req, res, next) => {
    res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
    res.setHeader('Pragma', 'no-cache');
    res.setHeader('Expires', '0');
    next();
  });
  
  app.use(express.static(path.join(__dirname, '../public')));
  
  app.get('/auth/discord', (req, res) => {
    if (!DISCORD_CLIENT_SECRET) {
      return res.status(500).json({ error: 'Discord OAuth not configured. Please set DISCORD_CLIENT_SECRET.' });
    }
    
    const state = crypto.randomBytes(16).toString('hex');
    req.session.oauthState = state;
    
    const redirectUri = `${BASE_URL}/auth/callback`;
    const scope = 'identify guilds';
    
    const authUrl = `https://discord.com/api/oauth2/authorize?` +
      `client_id=${DISCORD_CLIENT_ID}` +
      `&redirect_uri=${encodeURIComponent(redirectUri)}` +
      `&response_type=code` +
      `&scope=${encodeURIComponent(scope)}` +
      `&state=${state}`;
    
    res.redirect(authUrl);
  });
  
  app.get('/auth/callback', async (req, res) => {
    const { code, state } = req.query;
    
    if (!code) {
      return res.redirect('/?error=no_code');
    }
    
    if (state !== req.session.oauthState) {
      return res.redirect('/?error=invalid_state');
    }
    
    try {
      const redirectUri = `${BASE_URL}/auth/callback`;
      
      const tokenResponse = await fetch('https://discord.com/api/oauth2/token', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded'
        },
        body: new URLSearchParams({
          client_id: DISCORD_CLIENT_ID,
          client_secret: DISCORD_CLIENT_SECRET,
          grant_type: 'authorization_code',
          code,
          redirect_uri: redirectUri
        })
      });
      
      if (!tokenResponse.ok) {
        console.error('Token exchange failed:', await tokenResponse.text());
        return res.redirect('/?error=token_failed');
      }
      
      const tokens = await tokenResponse.json();
      
      const userResponse = await fetch('https://discord.com/api/users/@me', {
        headers: {
          Authorization: `Bearer ${tokens.access_token}`
        }
      });
      
      if (!userResponse.ok) {
        return res.redirect('/?error=user_fetch_failed');
      }
      
      const user = await userResponse.json();
      
      const guildsResponse = await fetch('https://discord.com/api/users/@me/guilds', {
        headers: {
          Authorization: `Bearer ${tokens.access_token}`
        }
      });
      
      let guilds = [];
      if (guildsResponse.ok) {
        guilds = await guildsResponse.json();
      }
      
      req.session.user = {
        id: user.id,
        username: user.username,
        discriminator: user.discriminator,
        avatar: user.avatar,
        globalName: user.global_name,
        accessToken: tokens.access_token,
        refreshToken: tokens.refresh_token,
        guilds: guilds.map(g => ({
          id: g.id,
          name: g.name,
          icon: g.icon,
          owner: g.owner,
          permissions: g.permissions,
          isAdmin: (parseInt(g.permissions) & 0x20) === 0x20
        }))
      };
      
      res.redirect('/dashboard');
      
    } catch (error) {
      console.error('OAuth callback error:', error);
      res.redirect('/?error=auth_failed');
    }
  });
  
  app.get('/auth/logout', (req, res) => {
    req.session.destroy();
    res.redirect('/');
  });
  
  app.get('/api/me', (req, res) => {
    if (!req.session.user) {
      return res.status(401).json({ error: 'Not authenticated' });
    }
    
    const user = req.session.user;
    res.json({
      id: user.id,
      username: user.username,
      discriminator: user.discriminator,
      avatar: user.avatar,
      globalName: user.globalName,
      avatarUrl: user.avatar 
        ? `https://cdn.discordapp.com/avatars/${user.id}/${user.avatar}.png`
        : `https://cdn.discordapp.com/embed/avatars/${parseInt(user.discriminator || '0') % 5}.png`,
      guilds: user.guilds
    });
  });
  
  // Profile handler function
  async function handleProfileRequest(req, res, userId) {
    if (!supabase) {
      return res.status(503).json({ error: 'Database not available' });
    }
    
    const targetUserId = userId || req.session.user?.id;
    if (!targetUserId) {
      return res.status(400).json({ error: 'User ID required' });
    }
    
    try {
      const { data: link } = await supabase
        .from('discord_links')
        .select('user_id')
        .eq('discord_id', targetUserId)
        .maybeSingle();
      
      if (!link) {
        return res.json({ linked: false, message: 'Discord account not linked' });
      }
      
      const { data: profile } = await supabase
        .from('user_profiles')
        .select('*')
        .eq('id', link.user_id)
        .maybeSingle();
      
      if (!profile) {
        return res.json({ linked: true, profile: null });
      }
      
      res.json({
        linked: true,
        profile: {
          id: profile.id,
          username: profile.username,
          xp: profile.xp || 0,
          prestigeLevel: profile.prestige_level || 0,
          totalXpEarned: profile.total_xp_earned || 0,
          dailyStreak: profile.daily_streak || 0,
          title: profile.title,
          bio: profile.bio,
          avatarUrl: profile.avatar_url,
          createdAt: profile.created_at
        }
      });
    } catch (error) {
      console.error('Profile fetch error:', error);
      res.status(500).json({ error: 'Failed to fetch profile' });
    }
  }
  
  // Route for current user's profile
  app.get('/api/profile', (req, res) => handleProfileRequest(req, res, null));
  
  // Route for specific user's profile
  app.get('/api/profile/:userId', (req, res) => handleProfileRequest(req, res, req.params.userId));
  
  app.get('/api/stats/:userId/:guildId', async (req, res) => {
    if (!supabase) {
      return res.status(503).json({ error: 'Database not available' });
    }
    
    const { userId, guildId } = req.params;
    
    try {
      const { data: link } = await supabase
        .from('discord_links')
        .select('user_id')
        .eq('discord_id', userId)
        .maybeSingle();
      
      if (!link) {
        return res.json({ stats: null });
      }
      
      const { data: stats } = await supabase
        .from('user_stats')
        .select('*')
        .eq('user_id', link.user_id)
        .eq('guild_id', guildId)
        .maybeSingle();
      
      res.json({ stats });
    } catch (error) {
      res.status(500).json({ error: 'Failed to fetch stats' });
    }
  });
  
  app.get('/api/achievements/:guildId', async (req, res) => {
    if (!supabase) {
      return res.status(503).json({ error: 'Database not available' });
    }
    
    const { guildId } = req.params;
    const userId = req.session.user?.id;
    
    try {
      const { data: achievements } = await supabase
        .from('achievements')
        .select('*')
        .eq('guild_id', guildId)
        .eq('hidden', false)
        .order('created_at', { ascending: true });
      
      let userAchievements = [];
      if (userId) {
        const { data: link } = await supabase
          .from('discord_links')
          .select('user_id')
          .eq('discord_id', userId)
          .maybeSingle();
        
        if (link) {
          const { data } = await supabase
            .from('user_achievements')
            .select('achievement_id, earned_at')
            .eq('user_id', link.user_id)
            .eq('guild_id', guildId);
          userAchievements = data || [];
        }
      }
      
      res.json({ achievements, userAchievements });
    } catch (error) {
      res.status(500).json({ error: 'Failed to fetch achievements' });
    }
  });
  
  app.get('/api/quests/:guildId', async (req, res) => {
    if (!supabase) {
      return res.status(503).json({ error: 'Database not available' });
    }
    
    const { guildId } = req.params;
    const userId = req.session.user?.id;
    
    try {
      const now = new Date().toISOString();
      const { data: quests } = await supabase
        .from('quests')
        .select('*')
        .eq('guild_id', guildId)
        .eq('active', true)
        .or(`starts_at.is.null,starts_at.lte.${now}`)
        .or(`expires_at.is.null,expires_at.gt.${now}`)
        .order('created_at', { ascending: false });
      
      let userQuests = [];
      if (userId) {
        const { data: link } = await supabase
          .from('discord_links')
          .select('user_id')
          .eq('discord_id', userId)
          .maybeSingle();
        
        if (link) {
          const { data } = await supabase
            .from('user_quests')
            .select('*')
            .eq('user_id', link.user_id)
            .eq('guild_id', guildId);
          userQuests = data || [];
        }
      }
      
      res.json({ quests, userQuests });
    } catch (error) {
      res.status(500).json({ error: 'Failed to fetch quests' });
    }
  });
  
  app.get('/api/shop/:guildId', async (req, res) => {
    if (!supabase) {
      return res.status(503).json({ error: 'Database not available' });
    }
    
    const { guildId } = req.params;
    
    try {
      const { data: items } = await supabase
        .from('shop_items')
        .select('*')
        .eq('guild_id', guildId)
        .eq('available', true)
        .order('price', { ascending: true });
      
      res.json({ items: items || [] });
    } catch (error) {
      res.status(500).json({ error: 'Failed to fetch shop items' });
    }
  });
  
  app.get('/api/inventory/:guildId', async (req, res) => {
    if (!supabase) {
      return res.status(503).json({ error: 'Database not available' });
    }
    
    const userId = req.session.user?.id;
    if (!userId) {
      return res.status(401).json({ error: 'Not authenticated' });
    }
    
    const { guildId } = req.params;
    
    try {
      const { data: link } = await supabase
        .from('discord_links')
        .select('user_id')
        .eq('discord_id', userId)
        .maybeSingle();
      
      if (!link) {
        return res.json({ inventory: [] });
      }
      
      const { data: inventory } = await supabase
        .from('user_inventory')
        .select(`
          *,
          shop_items (name, description, item_type, value)
        `)
        .eq('user_id', link.user_id)
        .eq('guild_id', guildId);
      
      res.json({ inventory: inventory || [] });
    } catch (error) {
      res.status(500).json({ error: 'Failed to fetch inventory' });
    }
  });
  
  app.get('/api/leaderboard/:guildId', async (req, res) => {
    if (!supabase) {
      return res.status(503).json({ error: 'Database not available' });
    }
    
    const { guildId } = req.params;
    const { type = 'all', limit = 50 } = req.query;
    
    try {
      let tableName = 'leaderboard_alltime';
      if (type === 'weekly') tableName = 'leaderboard_weekly';
      else if (type === 'monthly') tableName = 'leaderboard_monthly';
      
      const { data: leaderboard } = await supabase
        .from(tableName)
        .select(`
          *,
          user_profiles!inner (username, avatar_url, prestige_level)
        `)
        .eq('guild_id', guildId)
        .order('xp', { ascending: false })
        .limit(parseInt(limit));
      
      res.json({ leaderboard: leaderboard || [], type });
    } catch (error) {
      res.status(500).json({ error: 'Failed to fetch leaderboard' });
    }
  });
  
  app.get('/api/bot-stats', (req, res) => {
    const stats = {
      guilds: discordClient.guilds.cache.size,
      users: discordClient.guilds.cache.reduce((acc, g) => acc + g.memberCount, 0),
      commands: discordClient.commands?.size || 0,
      uptime: Math.floor(process.uptime()),
      status: discordClient.user ? 'online' : 'offline',
      botName: discordClient.user?.username || 'AeThex',
      botAvatar: discordClient.user?.displayAvatarURL() || null
    };
    res.json(stats);
  });
  
  app.get('/api/guild/:guildId/config', async (req, res) => {
    if (!supabase) {
      return res.status(503).json({ error: 'Database not available' });
    }
    
    const userId = req.session.user?.id;
    if (!userId) {
      return res.status(401).json({ error: 'Not authenticated' });
    }
    
    const { guildId } = req.params;
    
    const userGuild = req.session.user.guilds.find(g => g.id === guildId);
    if (!userGuild || !userGuild.isAdmin) {
      return res.status(403).json({ error: 'No admin access to this server' });
    }
    
    try {
      const { data: xpConfig } = await supabase
        .from('xp_config')
        .select('*')
        .eq('guild_id', guildId)
        .maybeSingle();
      
      const { data: serverConfig } = await supabase
        .from('server_config')
        .select('*')
        .eq('guild_id', guildId)
        .maybeSingle();
      
      res.json({ xpConfig, serverConfig });
    } catch (error) {
      res.status(500).json({ error: 'Failed to fetch config' });
    }
  });
  
  app.get('/health', (req, res) => {
    res.json({ 
      status: 'ok', 
      bot: discordClient.user ? 'online' : 'offline',
      uptime: process.uptime()
    });
  });
  
  app.get('/dashboard', (req, res) => {
    res.sendFile(path.join(__dirname, '../public/dashboard.html'));
  });
  
  app.get('/', (req, res) => {
    res.sendFile(path.join(__dirname, '../public/index.html'));
  });
  
  // Catch-all route for SPA - use middleware instead of wildcard
  app.use((req, res, next) => {
    if (req.method === 'GET' && !req.path.startsWith('/api/')) {
      res.sendFile(path.join(__dirname, '../public/index.html'));
    } else if (req.path.startsWith('/api/')) {
      res.status(404).json({ error: 'API endpoint not found' });
    } else {
      next();
    }
  });
  
  return app;
}

module.exports = { createWebServer };
