/**
 * Game Server - Based on Client LocalServer Infrastructure
 * Handles real-time multiplayer game instances and gameplay
 */

const express = require('express');
const http = require('http');
const errorReporter = require('./utils/ErrorReporter');
const socketIo = require('socket.io');
const cors = require('cors');
const helmet = require('helmet');
const compression = require('compression');
const mongoose = require('mongoose');
const fs = require('fs');
const path = require('path');
require('dotenv').config();

const logger = require('./utils/logger');
const connectDB = require('./config/database');
const PlayerData = require('./models/PlayerData');

// Import server systems for player initialization
const QuestSystem    = require('./systems/QuestSystem');
const SkillSystem    = require('./systems/SkillSystem');
const DungeonSystem  = require('./systems/DungeonSystem');
const CraftingSystem = require('./systems/CraftingSystem');
const IdleSystem     = require('./systems/IdleSystem');
const ItemSystem     = require('./systems/ItemSystem');
const ContentLoader  = require('./systems/ContentLoader');
const FleetSystem    = require('./systems/FleetSystem');
const { ResourceSystem, RESOURCE_CONFIG, RESOURCE_TYPES } = require('./systems/ResourceSystem');
const MissionSystem  = require('./systems/MissionSystem');
const { AllianceSystem } = require('./systems/AllianceSystem');
const { MarketSystem }   = require('./systems/MarketSystem');
const SocialSystem   = require('./systems/SocialSystem');
const { ReputationSystem } = require('./systems/ReputationSystem');
const GalaxyEventSystem = require('./systems/GalaxyEventSystem');
const { SeasonSystem }   = require('./systems/SeasonSystem');
const GalaxySystem   = require('./systems/GalaxySystem');
const { ResearchSystem } = require('./systems/ResearchSystem');

// ── Load all JSON game content before systems are initialised ──
const contentLoader = new ContentLoader();
contentLoader._loadSkills();
contentLoader._loadEnemies();
contentLoader._loadDungeons();
contentLoader._loadItems();
contentLoader._loadRecipes();
contentLoader._loadQuests();
contentLoader._loaded = true;
console.log(`[SERVER] Content loaded — ${contentLoader.skills.size} skills, ${contentLoader.items.size} items, ${contentLoader.recipes.size} recipes, ${contentLoader.quests.size} quests, ${contentLoader.dungeons.size} dungeons, ${contentLoader.enemies.size} enemies`);

// Initialize server systems — each receives contentLoader as its data source
const questSystem    = new QuestSystem(contentLoader);
const skillSystem    = new SkillSystem(contentLoader);
const dungeonSystem  = new DungeonSystem(contentLoader);
const craftingSystem = new CraftingSystem(contentLoader);
const idleSystem     = new IdleSystem();
const itemSystem     = new ItemSystem(contentLoader);

const fleetSystem    = new FleetSystem(contentLoader);
const resourceSystem = new ResourceSystem();
const missionSystem  = new MissionSystem();
const allianceSystem = new AllianceSystem();
const marketSystem   = new MarketSystem();
const socialSystem   = new SocialSystem();
const reputationSystem = new ReputationSystem();
const galaxyEventSystem = new GalaxyEventSystem();
const seasonSystem      = new SeasonSystem();
const galaxySystem   = new GalaxySystem();
const researchSystem = new ResearchSystem();
// Set server URL for ItemSystem
const SERVER_URL = process.env.SERVER_URL || 'https://dev.gameserver.galaxystrike.online';
itemSystem.setServerUrl(SERVER_URL);

const app = express();
const server = http.createServer(app);
const io = socketIo(server, {
  cors: {
    origin: [
      "http://localhost:3000", 
      "http://127.0.0.1:3000", 
      "file://",
      "https://dev.galaxystrike.online",
      "https://galaxystrike.online"
    ],
    methods: ["GET", "POST"],
    credentials: true
  }
});

// Pass io instance to systems that need it
dungeonSystem.setIO(io);

// Game state
const gameInstances = new Map();
const connectedClients = new Map();
let pvpChallenges = new Map(); // GDD §9.4 — active PvP challenge invites

// ── PvP battle simulation helper ─────────────────────────────────────────────
function _simulatePvpBattle(challenger, defender) {
  const rounds = [];
  let cHp = Math.max(1, (challenger.ship?.health || 1000) + (challenger.level || 1) * 50);
  let dHp = Math.max(1, (defender.ship?.health  || 1000) + (defender.level  || 1) * 50);
  const cAtk = Math.max(1, (challenger.ship?.attack  || 100) + (challenger.level || 1) * 10);
  const dAtk = Math.max(1, (defender.ship?.attack    || 100) + (defender.level  || 1) * 10);

  let round = 0;
  while (cHp > 0 && dHp > 0 && round < 20) {
    round++;
    const cDmg = Math.floor(cAtk * (0.85 + Math.random() * 0.3));
    const dDmg = Math.floor(dAtk * (0.85 + Math.random() * 0.3));
    dHp -= cDmg;
    cHp -= dDmg;
    rounds.push({ round, challengerDmg: cDmg, defenderDmg: dDmg, challengerHp: Math.max(0, cHp), defenderHp: Math.max(0, dHp) });
  }
  return { winner: cHp >= dHp ? 'challenger' : 'defender', rounds };
}

// ── Ship module slot helper ────────────────────────────────────────────────────
function _getShipSlots(ship) {
  const base = ['weapon_1', 'weapon_2', 'armor_1', 'engine', 'shield'];
  // Higher-rarity ships get extra slots
  const rarity = ship?.rarity || 'common';
  if (rarity === 'rare')      return [...base, 'special_1'];
  if (rarity === 'epic')      return [...base, 'special_1', 'special_2'];
  if (rarity === 'legendary') return [...base, 'special_1', 'special_2', 'special_3'];
  return base;
}

// Server-side shop item lookup using ItemSystem
function findShopItem(itemId) {
  return itemSystem.findShopItem(itemId);
}

// Middleware
app.use(cors({
  origin: [
    "http://localhost:3000", 
    "http://127.0.0.1:3000", 
    "file://",
    "https://dev.galaxystrike.online",
    "https://galaxystrike.online"
  ],
  credentials: true
}));
app.use(express.json({ limit: '10mb' }));
app.use(errorReporter.requestMiddleware());

// Health + metrics endpoint
app.get('/health', (req, res) => {
  res.json({ status: 'ok', ...errorReporter.getMetrics() });
});

// Serve static files from the client directory
app.use(express.static(path.join(__dirname, '../Client')));

// Serve ships from server-side storage
app.use('/images/ships', (req, res, next) => {
  const requestedPath = req.path;
  const serverImagePath = path.join(__dirname, 'assets/images/ships', requestedPath);
  
  console.log('[IMAGE SERVER] Ship requested:', requestedPath);
  
  if (fs.existsSync(serverImagePath)) {
    res.sendFile(serverImagePath);
  } else {
    const placeholderPath = path.join(__dirname, 'assets/images/ui/placeholder.png');
    res.sendFile(placeholderPath);
  }
});

// Serve weapons from server-side storage
app.use('/images/weapons', (req, res, next) => {
  const requestedPath = req.path;
  const serverImagePath = path.join(__dirname, 'assets/images/weapons', requestedPath);
  
  console.log('[IMAGE SERVER] Weapon requested:', requestedPath);
  
  if (fs.existsSync(serverImagePath)) {
    res.sendFile(serverImagePath);
  } else {
    const placeholderPath = path.join(__dirname, 'assets/images/ui/placeholder.png');
    res.sendFile(placeholderPath);
  }
});

// Serve armors from server-side storage
app.use('/images/armors', (req, res, next) => {
  const requestedPath = req.path;
  const serverImagePath = path.join(__dirname, 'assets/images/armors', requestedPath);
  
  console.log('[IMAGE SERVER] Armor requested:', requestedPath);
  
  if (fs.existsSync(serverImagePath)) {
    res.sendFile(serverImagePath);
  } else {
    const placeholderPath = path.join(__dirname, 'assets/images/ui/placeholder.png');
    res.sendFile(placeholderPath);
  }
});

// Serve other items (materials, consumables, cosmetics) from server-side storage
app.use('/images/items', (req, res, next) => {
  const requestedPath = req.path;
  const serverImagePath = path.join(__dirname, 'assets/images/items', requestedPath);
  
  console.log('[IMAGE SERVER] Item requested:', requestedPath);
  
  if (fs.existsSync(serverImagePath)) {
    res.sendFile(serverImagePath);
  } else {
    const placeholderPath = path.join(__dirname, 'assets/images/ui/placeholder.png');
    res.sendFile(placeholderPath);
  }
});

// Serve UI elements and icons from server-side storage
app.use('/images/ui', (req, res, next) => {
  const requestedPath = req.path;
  const serverImagePath = path.join(__dirname, 'assets/images/ui', requestedPath);
  
  if (fs.existsSync(serverImagePath)) {
    res.sendFile(serverImagePath);
  } else {
    res.status(404).send('UI asset not found');
  }
});
app.use(express.urlencoded({ extended: true }));

// Health check endpoint
app.get('/health', (req, res) => {
  res.status(200).json({ 
    status: 'OK', 
    timestamp: new Date().toISOString(),
    uptime: process.uptime(),
    mode: 'game-server',
    activeInstances: gameInstances.size,
    connectedClients: connectedClients.size
  });
});

// API version endpoint
app.get('/api/ssc/version', (req, res) => {
  res.status(200).json({ 
    version: '1.0.0',
    service: 'galaxystrikeonline-game-server',
    timestamp: new Date().toISOString(),
    mode: 'multiplayer'
  });
});

// Shop API endpoints
app.get('/api/shop/items', (req, res) => {
  try {
    res.status(200).json(itemSystem.buildShopResponse());
  } catch (error) {
    console.error('[GAME SERVER] Error fetching shop items:', error);
    res.status(500).json({ success: false, error: 'Failed to fetch shop items' });
  }
});

app.get('/api/shop/items/:category', (req, res) => {
  try {
    const { category } = req.params;
    res.status(200).json({
      success: true,
      category,
      items: itemSystem.getRandomItemsByCategory(category),
      timestamp: new Date().toISOString()
    });
  } catch (error) {
    console.error('[GAME SERVER] Error fetching category items:', error);
    res.status(500).json({ success: false, error: 'Failed to fetch category items' });
  }
});

app.get('/api/items/:itemId', (req, res) => {
  try {
    const { itemId } = req.params;
    
    // Find item across all categories
    const allItems = itemSystem.getAllItems();
    let item = null;
    
    for (const [category, items] of Object.entries(allItems)) {
      item = items.find(i => i.id === itemId);
      if (item) break;
    }
    
    if (!item) {
      return res.status(404).json({
        success: false,
        error: 'Item not found'
      });
    }
    
    res.status(200).json({
      success: true,
      item: item,
      timestamp: new Date().toISOString()
    });
  } catch (error) {
    console.error('[GAME SERVER] Error fetching item details:', error);
    res.status(500).json({
      success: false,
      error: 'Failed to fetch item details'
    });
  }
});

// Game data endpoints (similar to LocalServer)
app.post('/api/game/player/:id/save', (req, res) => {
  const playerId = req.params.id;
  const playerData = req.body;
  
  // Store player data in game instance
  if (connectedClients.has(playerId)) {
    connectedClients.get(playerId).playerData = playerData;
    
    // Broadcast to other players in same instance
    const instanceId = connectedClients.get(playerId).instanceId;
    if (gameInstances.has(instanceId)) {
      io.to(instanceId).emit('playerDataUpdated', {
        playerId,
        timestamp: Date.now()
      });
    }
  }
  
  res.status(200).json({
    success: true,
    message: 'Player data saved to game server'
  });
});

app.get('/api/game/player/:id', (req, res) => {
  const playerId = req.params.id;
  
  if (connectedClients.has(playerId)) {
    const playerData = connectedClients.get(playerId).playerData;
    res.status(200).json({
      success: true,
      player: playerData
    });
  } else {
    res.status(404).json({
      success: false,
      error: 'Player not connected to game server'
    });
  }
});

// Game system routes
app.use('/api/crafting', require('./routes/crafting'));
app.use('/api/quests', require('./routes/quests'));
app.use('/api/skills', require('./routes/skills'));
app.use('/api/ships', require('./routes/ships'));
app.use('/api/idle', require('./routes/idle'));
app.use('/api/dungeons', require('./routes/dungeons'));
app.use('/api/base', require('./routes/base'));

// Socket.IO handlers (based on LocalServer)
io.on('connection', (socket) => {
  console.log('[GAME SERVER] === NEW CLIENT CONNECTION ===');
  console.log('[GAME SERVER] Client connected:', socket.id);

  // ── Rate limiter: GDD §18.4 — max 20 events/sec per socket ──────────
  {
    const RL_MAX = 20, RL_WIN = 1000;
    let rlCount = 0, rlReset = Date.now() + RL_WIN;
    const _origOn = socket.on.bind(socket);
    const SYSTEM_EVENTS = new Set(['connect','disconnect','error','reconnect']);
    socket.on = function(event, handler) {
      if (SYSTEM_EVENTS.has(event)) return _origOn(event, handler);
      return _origOn(event, function(...args) {
        const now = Date.now();
        if (now > rlReset) { rlCount = 0; rlReset = now + RL_WIN; }
        if (++rlCount > RL_MAX) {
          console.warn(`[RATE_LIMIT] ${socket.id} dropped '${event}' (${rlCount}/s)`);
          return;
        }
        handler.apply(this, args);
      });
    };
  }
  connectedClients.set(socket.id, {
    connectedAt: Date.now(),
    playerData: null,
    instanceId: null
  });
  
  console.log('[GAME SERVER] Waiting for authentication from:', socket.id);
  
  // Update player count on API server when new player connects
  updatePlayerCountOnAPI();
  
  // Add timeout for authentication
  const authTimeout = setTimeout(() => {
    const clientData = connectedClients.get(socket.id);
    if (clientData && !clientData.userId) {
      console.log('[GAME SERVER] Authentication timeout for:', socket.id);
      socket.emit('authenticated', {
        success: false,
        error: 'Authentication timeout'
      });
      socket.disconnect();
    }
  }, 10000); // 10 seconds timeout
  
  // Clear timeout when authenticated
  socket.on('authenticated', () => {
    clearTimeout(authTimeout);
  });
  
  // Log all incoming events for debugging
  socket.onAny((eventName, ...args) => {
    if (eventName !== 'ping' && eventName !== 'pong') {
      console.log(`[GAME SERVER] Event received: ${eventName} from ${socket.id}`, args);
    }
  });
  
  // Authentication (similar to LocalServer)
  socket.on('authenticate', async (data) => {
    console.log('[GAME SERVER] Authenticating client:', socket.id, data);
    
    try {
      // Check database connection first
      if (mongoose.connection.readyState !== 1) {
        console.error('[GAME SERVER] Database not connected, authentication failed');
        socket.emit('authenticated', {
          success: false,
          error: 'Database not available'
        });
        return;
      }
      
      console.log('[GAME SERVER] Database is connected, proceeding with authentication');
      
      // Load player data from database
      const playerData = await loadPlayerData(data.userId || socket.id, data.username || 'Game Player');
      
      if (playerData) {
        console.log('[GAME SERVER] Player data loaded successfully:', playerData.username);
        
        // Store player data in client connection
        const clientData = connectedClients.get(socket.id);
        if (clientData) {
          clientData.playerData = playerData;
          clientData.userId = playerData.userId;
          clientData.username = playerData.username;
        } else {
          console.error('[GAME SERVER] No client data found for socket:', socket.id);
        }
        
        // Join server
        playerData.joinServer(`devgame-server-${PORT}`);
        
        // Update last login time + daily login streak gems (GDD §3 v3.2)
        if (!playerData.stats) playerData.stats = {};
        const now = new Date();
        const lastLogin = playerData.stats.lastLogin ? new Date(playerData.stats.lastLogin) : null;
        let streakGems = 0;
        if (lastLogin) {
          const hoursSince = (now - lastLogin) / 3600000;
          if (hoursSince >= 20 && hoursSince <= 48) {
            // Consecutive day login — increment streak
            playerData.stats.loginStreak = Math.min(30, (playerData.stats.loginStreak || 0) + 1);
          } else if (hoursSince > 48) {
            // Streak broken
            playerData.stats.loginStreak = 1;
          }
        } else {
          playerData.stats.loginStreak = 1;
        }
        // Gem reward: day 1=0, day 3=1, day 7=3, day 14=5, day 30=10
        const streak = playerData.stats.loginStreak || 1;
        if      (streak >= 30) streakGems = 10;
        else if (streak >= 14) streakGems = 5;
        else if (streak >= 7)  streakGems = 3;
        else if (streak >= 3)  streakGems = 1;
        if (streakGems > 0) {
          playerData.stats.gems = (playerData.stats.gems || 0) + streakGems;
        }
        playerData.stats.lastLogin = now.toISOString();
        
        // Ensure all required fields exist (for existing players)
        if (playerData.stats.totalExperience === undefined) playerData.stats.totalExperience = playerData.stats.experience || 0;
        if (playerData.stats.gems === undefined || playerData.stats.gems === 0) playerData.stats.gems = 50; // Restore gems if wiped
        if (playerData.stats.skillPoints === undefined) playerData.stats.skillPoints = 0;
        if (playerData.stats.totalKills === undefined) playerData.stats.totalKills = 0;
        if (playerData.stats.questsCompleted === undefined) playerData.stats.questsCompleted = 0;
        
        // Ensure idle system production rates are initialized
        idleSystem.initializePlayerData(playerData.userId);
        console.log('[GAME SERVER] Idle system initialized for player:', playerData.username);
        
        await savePlayerData(playerData.userId, playerData);
        
        // Auto-collect any completed fleet missions (GDD §8.3)
        try {
          if (playerData.fleetMissions?.length > 0) {
            const mResults = missionSystem.collectMissions(playerData, resourceSystem);
            if (mResults.length > 0) {
              await savePlayerData(playerData.userId, playerData);
              mResults.forEach(r => socket.emit('mission_completed', r));
            }
          }
        } catch(me) { console.error('[AUTH MISSION COLLECT]', me.message); }

        // Calculate pending offline rewards to show on dashboard
        const pendingOffline = idleSystem.calculateOfflineRewards(playerData.userId);
        if (pendingOffline.offlineTime > 0) {
          playerData.stats.offlineTime = pendingOffline.offlineTime;
          playerData.stats.offlineCredits = pendingOffline.rewards?.credits || 0;
        } else {
          playerData.stats.offlineTime = 0;
          playerData.stats.offlineCredits = 0;
        }

        // In production, validate with API server
        socket.emit('authenticated', {
          success: true,
          user: {
            id: playerData.userId,
            username: playerData.username,
            token: 'game-token-' + Date.now()
          },
          playerData: {
            ...playerData.toObject(),
            serverTimestamp: Date.now(),
            serverTimezone: 'UTC'
          },
          loginStreak: playerData.stats.loginStreak || 1,
          streakGemsAwarded: streakGems || 0,
        });
        
        console.log(`[GAME SERVER] ${playerData.username} authenticated with Level ${playerData.stats.level}`);
      } else {
        console.error('[GAME SERVER] Failed to load player data');
        socket.emit('authenticated', {
          success: false,
          error: 'Failed to load player data'
        });
      }
    } catch (error) {
      console.error('[GAME SERVER] Authentication error:', error);
      console.error('[GAME INITIALIZER] Error stack:', error.stack);
      socket.emit('authenticated', {
        success: false,
        error: 'Authentication failed: ' + error.message
      });
    }
  });
  
  // Game data events (similar to LocalServer)
  socket.on('saveGameData', async (data) => {
    console.log('[GAME SERVER] Saving game data for:', socket.id);
    
    const clientData = connectedClients.get(socket.id);
    if (clientData && clientData.userId) {
      try {
        // Validate the data before saving
        if (!data || typeof data !== 'object') {
          console.warn('[GAME SERVER] Invalid game data received, skipping save');
          socket.emit('gameDataSaved', { success: false, error: 'Invalid game data' });
          return;
        }
        
        // Update player data with new game data
        const updatedPlayerData = { ...clientData.playerData, ...data };
        clientData.playerData = updatedPlayerData;
        
        // Save to database
        const success = await savePlayerData(clientData.userId, updatedPlayerData);
        
        socket.emit('gameDataSaved', { 
          success: success,
          message: success ? 'Game saved to server!' : 'Failed to save to server'
        });
        
        console.log(`[GAME SERVER] Saved game data for ${clientData.username}`);
      } catch (error) {
        console.error('[GAME SERVER] Error saving game data:', error);
        socket.emit('gameDataSaved', { success: false, error: 'Failed to save data' });
      }
    } else {
      console.warn('[GAME SERVER] No client data or user ID found for saveGameData');
      socket.emit('gameDataSaved', { success: false, error: 'Player not authenticated' });
    }
  });

  socket.on('loadGameData', (data) => {
    console.log('[GAME SERVER] Loading game data for:', socket.id);
    
    const playerData = connectedClients.get(socket.id)?.playerData || {};
    
    socket.emit('gameDataLoaded', {
      success: true,
      data: playerData
    });
  });
  
  // Test idle system manually
  socket.on('testIdleRewards', (data) => {
    console.log('[GAME SERVER] Testing idle rewards for:', socket.id);
    
    const clientData = connectedClients.get(socket.id);
    if (clientData && clientData.userId) {
      const onlineRewards = idleSystem.generateOnlineIdleRewards(clientData.userId, 10000);
      console.log('[GAME SERVER] Test idle rewards:', onlineRewards);
      
      socket.emit('testIdleRewards', {
        rewards: onlineRewards,
        productionRates: idleSystem.playerProductionRates.get(clientData.userId)
      });
    } else {
      socket.emit('testIdleRewards', { error: 'Not authenticated' });
    }
  });
  
  // Shop and item system events
  socket.on('ping', (data) => {
    console.log('[GAME SERVER] Ping received from:', socket.id, data);
    socket.emit('pong', { 
      timestamp: Date.now(),
      received: data.timestamp,
      serverTime: new Date().toISOString()
    });
  });
  
  socket.on('getShopItems', () => {
    try {
      socket.emit('shopItemsReceived', itemSystem.buildShopResponse());
    } catch (error) {
      console.error('[GAME SERVER] getShopItems error:', error);
      socket.emit('shopItemsReceived', { success: false, error: 'Failed to load shop items' });
    }
  });
  
  socket.on('getShopCategory', (data) => {
    try {
      const { category } = data || {};
      if (!category) {
        socket.emit('shopCategoryReceived', { success: false, error: 'Category required' });
        return;
      }
      socket.emit('shopCategoryReceived', {
        success: true,
        category,
        items: itemSystem.getRandomItemsByCategory(category),
        timestamp: new Date().toISOString()
      });
    } catch (error) {
      console.error('[GAME SERVER] getShopCategory error:', error);
      socket.emit('shopCategoryReceived', { success: false, error: 'Failed to load category items' });
    }
  });
  
  socket.on('getItemDetails', (data) => {
    const { itemId } = data || {};
    if (!itemId) {
      socket.emit('itemDetailsReceived', { success: false, error: 'Item ID required' });
      return;
    }
    socket.emit('itemDetailsReceived', itemSystem.buildItemDetailResponse(itemId));
  });
  
  // Game-specific events
  socket.on('joinGameInstance', (data) => {
    const { instanceId } = data;
    
    if (!gameInstances.has(instanceId)) {
      gameInstances.set(instanceId, {
        id: instanceId,
        players: new Set(),
        createdAt: Date.now()
      });
    }
    
    const instance = gameInstances.get(instanceId);
    instance.players.add(socket.id);
    
    connectedClients.get(socket.id).instanceId = instanceId;
    socket.join(instanceId);
    
    socket.emit('joinedGameInstance', {
      instanceId,
      playerCount: instance.players.size
    });
    
    // Notify other players
    socket.to(instanceId).emit('playerJoinedInstance', {
      playerId: socket.id,
      playerCount: instance.players.size
    });
  });
  
  socket.on('leaveGameInstance', (data) => {
    const clientData = connectedClients.get(socket.id);
    if (clientData && clientData.instanceId) {
      const instance = gameInstances.get(clientData.instanceId);
      if (instance) {
        instance.players.delete(socket.id);
        socket.leave(clientData.instanceId);
        
        // Clean up empty instances
        if (instance.players.size === 0) {
          gameInstances.delete(clientData.instanceId);
        }
        
        // Notify other players
        socket.to(clientData.instanceId).emit('playerLeftInstance', {
          playerId: socket.id,
          playerCount: instance.players.size
        });
      }
      
      clientData.instanceId = null;
    }
  });
  
  socket.on('gameAction', (data) => {
    const clientData = connectedClients.get(socket.id);
    if (clientData && clientData.instanceId) {
      // Broadcast game action to other players in same instance
      socket.to(clientData.instanceId).emit('gameAction', {
        playerId: socket.id,
        action: data,
        timestamp: Date.now()
      });
    }
  });
  
  // Idle rewards events
  socket.on('claimOfflineRewards', async (data) => {
    console.log('[GAME SERVER] Claiming offline rewards for:', socket.id);
    
    const clientData = connectedClients.get(socket.id);
    if (!clientData || !clientData.userId) {
      socket.emit('offlineRewardsClaimed', { success: false, error: 'Not authenticated' });
      return;
    }
    
    try {
      const playerData = await loadPlayerData(clientData.userId, clientData.username || 'Player');
      const offlineRewards = idleSystem.calculateOfflineRewards(clientData.userId);
      
      if (offlineRewards.offlineTime > 0 && offlineRewards.rewards.credits > 0) {
        // Apply rewards to player
        playerData.stats.credits = (playerData.stats.credits || 0) + offlineRewards.rewards.credits;
        playerData.stats.experience = (playerData.stats.experience || 0) + offlineRewards.rewards.experience;
        
        // Update idle system data
        if (!playerData.idleSystem) playerData.idleSystem = {};
        playerData.idleSystem.lastActive = new Date().toISOString();
        playerData.idleSystem.totalOfflineTime += offlineRewards.offlineTime;
        playerData.idleSystem.totalIdleCredits += offlineRewards.rewards.credits;
        
        await savePlayerData(clientData.userId, playerData);
        
        socket.emit('offlineRewardsClaimed', { 
          success: true, 
          rewards: offlineRewards.rewards,
          offlineTime: offlineRewards.offlineTime
        });
        
        console.log(`[GAME SERVER] Offline rewards claimed for ${clientData.username}: ${offlineRewards.rewards.credits} credits`);
      } else {
        socket.emit('offlineRewardsClaimed', { 
          success: true, 
          rewards: { credits: 0, experience: 0, energy: 0 },
          offlineTime: 0
        });
      }
    } catch (error) {
      console.error('[GAME SERVER] Error claiming offline rewards:', error);
      socket.emit('offlineRewardsClaimed', { success: false, error: 'Failed to claim rewards' });
    }
  });
  
  // Shop purchase events
  socket.on('purchaseItem', async (data) => {
    const clientData = connectedClients.get(socket.id);
    if (!clientData?.userId) {
      socket.emit('purchaseCompleted', { success: false, error: 'Not authenticated' });
      return;
    }

    try {
      const { itemId, quantity = 1 } = data || {};
      if (!itemId) {
        socket.emit('purchaseCompleted', { success: false, error: 'Item ID required' });
        return;
      }

      const playerData = await loadPlayerData(clientData.userId, clientData.username || 'Player');
      if (!playerData) {
        socket.emit('purchaseCompleted', { success: false, error: 'Failed to load player data' });
        return;
      }

      // Server-side validation: item must exist and be in the shop
      const item = itemSystem.findShopItem(itemId);
      if (!item) {
        socket.emit('purchaseCompleted', { success: false, error: 'Item not found in shop' });
        return;
      }

      // Block re-purchase of already-owned cosmetics and decorations
      if (item.type === 'cosmetic' && playerData.ownedCosmetics?.includes(item.id)) {
        socket.emit('purchaseCompleted', { success: false, error: 'You already own this cosmetic' });
        return;
      }
      if (item.type === 'decoration') {
        const sb = playerData.starbase || {};
        if (item.subtype === 'wallpaper' && sb.ownedWallpapers?.includes(item.id)) {
          socket.emit('purchaseCompleted', { success: false, error: 'You already own this wallpaper' });
          return;
        }
        if (item.subtype === 'room_unlock' && item.roomId && sb.unlockedRooms?.includes(item.roomId)) {
          socket.emit('purchaseCompleted', { success: false, error: 'You already own this room' });
          return;
        }
      }

      // Validate affordability
      const validation = itemSystem.validatePurchase(item, playerData.stats, quantity);
      if (!validation.valid) {
        socket.emit('purchaseCompleted', { success: false, error: validation.error });
        return;
      }

      // Apply purchase (deducts currency + grants item in playerData)
      const purchaseSummary = itemSystem.applyPurchase(item, playerData, quantity);

      await savePlayerData(clientData.userId, playerData);
      clientData.playerData = playerData;

      socket.emit('purchaseCompleted', {
        success:    true,
        item:       itemSystem.buildItemDetailResponse(item.id).item,
        quantity,
        totalCost:  purchaseSummary.totalCost,
        currency:   purchaseSummary.currency,
        newBalance: purchaseSummary.newBalance
      });

      console.log(`[GAME SERVER] Purchase: ${clientData.username} bought ${item.name} x${quantity} for ${purchaseSummary.totalCost} ${purchaseSummary.currency}`);
      broadcastEconomyUpdate(socket.id);

    } catch (error) {
      console.error('[GAME SERVER] purchaseItem error:', error);
      socket.emit('purchaseCompleted', { success: false, error: 'Purchase failed: ' + error.message });
    }
  });

  // ── Starbase customisation ──────────────────────────────────────────────
  socket.on('get_starbase_data', async () => {
    const client = connectedClients.get(socket.id);
    if (!client?.userId) { socket.emit('starbase_data', { success: false }); return; }
    try {
      const pd = await loadPlayerData(client.userId, client.username || 'Player');
      const sb = pd?.starbase || { wallpaper: null, ownedWallpapers: [], unlockedRooms: [] };
      const catalog = itemSystem.getAllItems().filter(i => i.type === 'decoration');
      socket.emit('starbase_data', { success: true, starbase: sb, catalog });
    } catch (err) {
      console.error('[GAME SERVER] get_starbase_data error:', err);
      socket.emit('starbase_data', { success: false, error: err.message });
    }
  });

  socket.on('set_wallpaper', async (data) => {
    const { wallpaperId, roomId } = data || {};
    const client = connectedClients.get(socket.id);
    if (!client?.userId) { socket.emit('wallpaper_set', { success: false, error: 'Not authenticated' }); return; }
    try {
      const pd = await loadPlayerData(client.userId, client.username || 'Player');
      if (!pd) { socket.emit('wallpaper_set', { success: false, error: 'Player not found' }); return; }
      if (!pd.starbase) pd.starbase = { wallpaper: null, ownedWallpapers: [], unlockedRooms: [] };
      if (wallpaperId === null) {
        if (roomId) {
          if (!pd.starbase.roomWallpapers) pd.starbase.roomWallpapers = {};
          delete pd.starbase.roomWallpapers[roomId];
        } else {
          pd.starbase.wallpaper = null;
        }
      } else {
        if (!pd.starbase.ownedWallpapers?.includes(wallpaperId)) {
          socket.emit('wallpaper_set', { success: false, error: 'Wallpaper not owned' }); return;
        }
        if (roomId) {
          if (!pd.starbase.roomWallpapers) pd.starbase.roomWallpapers = {};
          pd.starbase.roomWallpapers[roomId] = wallpaperId;
        } else {
          pd.starbase.wallpaper = wallpaperId;
        }
      }
      await savePlayerData(client.userId, pd);
      client.playerData = pd;
      socket.emit('wallpaper_set', { success: true, starbase: pd.starbase });
    } catch (err) {
      console.error('[GAME SERVER] set_wallpaper error:', err);
      socket.emit('wallpaper_set', { success: false, error: err.message });
    }
  });

  socket.on('get_skills', () => {
    console.log('[GAME SERVER] Sending skill definitions to:', socket.id);
    try {
      const skills = skillSystem.getAllSkills();
      socket.emit('skills_data', skills);
    } catch (error) {
      console.error('[GAME SERVER] Error sending skills:', error);
      socket.emit('skills_data', []);
    }
  });

  socket.on('get_recipes', () => {
    console.log('[GAME SERVER] Sending crafting recipes to:', socket.id);
    try {
      const recipes = craftingSystem.getAllRecipes();
      socket.emit('recipes_data', recipes);
    } catch (error) {
      console.error('[GAME SERVER] Error sending recipes:', error);
      socket.emit('recipes_data', []);
    }
  });

  // ── CRAFTING: craft_item (GDD §11) — timed queue ────────────────────────
  // Materials are consumed immediately; item arrives after craft_time_seconds.
  // Skill level reduces time: time = base * (1 - 0.01 * craftingLevel) capped at 50% reduction.
  socket.on('craft_item', async ({ recipeId } = {}) => {
    if (!socket.userId) return socket.emit('craft_result', { success: false, error: 'Not authenticated' });
    if (!recipeId)      return socket.emit('craft_result', { success: false, error: 'No recipe specified' });

    try {
      const playerData = await PlayerData.findOne({ userId: socket.userId });
      if (!playerData) return socket.emit('craft_result', { success: false, error: 'Player not found' });

      const recipe = craftingSystem.getRecipe(recipeId);
      if (!recipe) return socket.emit('craft_result', { success: false, error: 'Recipe not found' });

      // Check max queue size (5 simultaneous crafts)
      const crafting = playerData.crafting || {};
      const queue    = Array.isArray(crafting.queue) ? crafting.queue : [];
      if (queue.length >= 5)
        return socket.emit('craft_result', { success: false, error: 'Crafting queue full (max 5)' });

      // Server-side material check
      const check = craftingSystem.checkMaterials(recipeId, playerData.inventory || { items: [] });
      if (!check.canCraft)
        return socket.emit('craft_result', { success: false, error: 'Missing materials', missing: check.missing });

      // Consume materials now
      const inputs = recipe.recipe?.inputs || recipe.inputs || {};
      const inv    = playerData.inventory || { items: [] };
      for (const [itemId, qty] of Object.entries(inputs)) {
        craftingSystem._removeItems(inv, itemId, qty);
      }
      playerData.inventory = inv;

      // Calculate craft time with skill reduction
      const skillLevel  = crafting.skill || 1;
      const reduction   = Math.min(0.5, skillLevel * 0.01); // 1% per level, cap 50%
      const baseTimeSec = recipe.recipe?.craft_time_seconds
                       || recipe.recipe?.alloy_time_seconds
                       || recipe.recipe?.smelt_time_seconds
                       || recipe.recipe?.process_time_seconds
                       || recipe.recipe?.cook_time_seconds
                       || recipe.recipe?.harvest_time_seconds
                       || 30; // default 30s
      const timeSec     = Math.max(5, Math.round(baseTimeSec * (1 - reduction)));
      const completesAt = Date.now() + timeSec * 1000;

      // Queue the craft
      const craftJobId = `craft_${Date.now()}_${Math.random().toString(36).slice(2,6)}`;
      const outputs    = recipe.recipe?.output || recipe.output || {};
      queue.push({
        jobId:       craftJobId,
        recipeId,
        outputs,
        xpGain:      recipe.craft?.xp || recipe.xp || 10,
        startedAt:   Date.now(),
        completesAt,
        timeSec,
      });

      // Update skill XP on start (small preview XP — full XP on collect)
      crafting.queue      = queue;
      crafting.skill      = crafting.skill || 1;
      crafting.experience = crafting.experience || 0;
      playerData.crafting = crafting;

      playerData.markModified('inventory');
      playerData.markModified('crafting');
      await playerData.save();

      socket.emit('craft_result', {
        success:    true,
        queued:     true,
        jobId:      craftJobId,
        recipeId,
        timeSec,
        completesAt,
        queueLength: queue.length,
      });
      socket.emit('inventory_update', {
        items:   playerData.inventory?.items || [],
        maxSize: playerData.inventory?.maxSize || 50,
      });
      socket.emit('craft_queue_update', { queue });
      console.log(`[CRAFTING] ${socket.username} queued ${recipeId} — completes in ${timeSec}s`);
    } catch (err) {
      console.error('[CRAFTING] craft_item error:', err);
      socket.emit('craft_result', { success: false, error: 'Server error' });
    }
  });

  // ── CRAFTING: collect_craft — collect a completed craft job ─────────────
  socket.on('collect_craft', async ({ jobId } = {}) => {
    if (!socket.userId || !jobId) return;
    try {
      const playerData = await PlayerData.findOne({ userId: socket.userId });
      if (!playerData) return;
      const crafting = playerData.crafting || {};
      const queue    = Array.isArray(crafting.queue) ? crafting.queue : [];
      const idx      = queue.findIndex(j => j.jobId === jobId);
      if (idx < 0) return socket.emit('collect_craft_result', { success: false, error: 'Job not found' });

      const job = queue[idx];
      if (Date.now() < job.completesAt)
        return socket.emit('collect_craft_result', { success: false, error: 'Not ready yet', remainingMs: job.completesAt - Date.now() });

      // Add outputs to inventory
      const inv = playerData.inventory || { items: [] };
      const outputItems = [];
      for (const [itemId, qty] of Object.entries(job.outputs || {})) {
        const item = { id: itemId, itemId, quantity: qty, obtainedAt: Date.now(), source: 'crafting' };
        (inv.items || inv).push(item);
        outputItems.push(item);
      }
      playerData.inventory = inv;

      // Award XP and skill up
      crafting.experience = (crafting.experience || 0) + job.xpGain;
      const newSkill = Math.min(50, Math.floor(crafting.experience / 200) + 1);
      crafting.skill = newSkill;
      crafting.totalCrafted = (crafting.totalCrafted || 0) + 1;
      queue.splice(idx, 1);
      crafting.queue = queue;
      playerData.crafting = crafting;

      playerData.markModified('inventory');
      playerData.markModified('crafting');
      await playerData.save();

      socket.emit('collect_craft_result', {
        success: true, jobId, output: outputItems,
        xpGained: job.xpGain, craftingLevel: newSkill,
        craftingXp: crafting.experience,
      });
      socket.emit('inventory_update', { items: inv.items || [], maxSize: playerData.inventory?.maxSize || 50 });
      socket.emit('craft_queue_update', { queue });
    } catch (err) { socket.emit('collect_craft_result', { success: false, error: 'Server error' }); }
  });

  // ── CRAFTING: get_craft_queue — fetch current queue state ───────────────
  socket.on('get_craft_queue', async () => {
    if (!socket.userId) return;
    try {
      const playerData = await PlayerData.findOne({ userId: socket.userId });
      if (!playerData) return;
      const queue = Array.isArray(playerData.crafting?.queue) ? playerData.crafting.queue : [];
      socket.emit('craft_queue_update', { queue, skill: playerData.crafting?.skill || 1, xp: playerData.crafting?.experience || 0 });
    } catch (err) {}
  });

  // ── CRAFTING: check_craft (can player craft a recipe?) ──────────────────
  socket.on('check_craft', async ({ recipeId } = {}) => {
    if (!socket.userId || !recipeId) return;
    try {
      const playerData = await PlayerData.findOne({ userId: socket.userId });
      if (!playerData) return;
      const recipe = craftingSystem.getRecipe(recipeId);
      const result = craftingSystem.checkMaterials(recipeId, playerData.inventory || { items: [] });
      const skillLevel = playerData.crafting?.skill || 1;
      const reduction  = Math.min(0.5, skillLevel * 0.01);
      const baseTime   = recipe?.recipe?.craft_time_seconds || recipe?.recipe?.alloy_time_seconds
                      || recipe?.recipe?.smelt_time_seconds || 30;
      const timeSec    = Math.max(5, Math.round(baseTime * (1 - reduction)));
      socket.emit('craft_check_result', { recipeId, ...result, timeSec, skillLevel });
    } catch (err) {
      console.error('[CRAFTING] check_craft error:', err);
    }
  });

  socket.on('get_dungeons', () => {
    console.log('[GAME SERVER] Sending dungeons data to:', socket.id);
    const dungeons = dungeonSystem.getDungeonsGroupedByDifficulty();
    socket.emit('dungeons_data', dungeons);
  });
  
  socket.on('get_enemy_templates', () => {
    // Client expects { [id]: enemyTemplate } — convert array to keyed object
    const arr = dungeonSystem.getEnemyTemplates();
    const templates = {};
    for (const e of arr) templates[e.id] = e;
    socket.emit('enemy_templates_data', templates);
  });
  
  // Economy System Packet Handlers
  socket.on('get_economy_data', () => {
    console.log('[GAME SERVER] Sending economy data to:', socket.id);
    const clientData = connectedClients.get(socket.id);
    if (clientData && clientData.playerData) {
      const economyData = {
        credits: clientData.playerData.stats.credits || 0,
        gems: clientData.playerData.stats.gems || 0
      };
      socket.emit('economy_data', economyData);
    }
  });
  
  // Function to broadcast economy updates to specific client
  function broadcastEconomyUpdate(socketId) {
    const clientData = connectedClients.get(socketId);
    if (clientData && clientData.playerData) {
      const economyData = {
        credits: clientData.playerData.stats.credits || 0,
        gems: clientData.playerData.stats.gems || 0
      };
      io.to(socketId).emit('economy_data', economyData);
      console.log('[GAME SERVER] Broadcasted economy update to:', socketId, economyData);
    }
  }
  
  // ════════════════════════════════════════════════════════════════════════
  // BUILDINGS / BASE HANDLERS
  // ════════════════════════════════════════════════════════════════════════

  // Building definitions (in-memory; extend to JSON data later)
  const BUILDING_DEFS = {
    // GDD §6.2 — buildings cost metal/gas/crystal in addition to credits
    command_center:   { name:'Command Center',   maxLevel:20, baseCost:{credits:0,    metal:100                   }, timeBase:60,  effects:{buildSlots:1},        icon:'fa-satellite-dish', description:'Gates all other building levels.' },
    mining_facility:  { name:'Mining Facility',  maxLevel:20, baseCost:{credits:0,    metal:300                   }, timeBase:90,  effects:{metalPerHr:100},       icon:'fa-industry',       description:'+15% metal/hr per level.' },
    gas_extractor:    { name:'Gas Extractor',    maxLevel:20, baseCost:{credits:0,    metal:200,  gas:100         }, timeBase:90,  effects:{gasPerHr:80},          icon:'fa-cloud',          description:'+15% gas/hr per level.' },
    power_reactor:    { name:'Power Reactor',    maxLevel:20, baseCost:{credits:0,    metal:400,  crystal:50      }, timeBase:120, effects:{energyPerHr:200},      icon:'fa-bolt',           description:'+200 energy/hr per level.' },
    shipyard:         { name:'Shipyard',         maxLevel:15, baseCost:{credits:500,  metal:1000, gas:300         }, timeBase:180, effects:{buildSpeed:10},        icon:'fa-anchor',         description:'Build ships; each level +10% speed.' },
    research_lab:     { name:'Research Lab',     maxLevel:20, baseCost:{credits:300,  metal:600,  crystal:100     }, timeBase:150, effects:{researchSpeed:8},      icon:'fa-flask',          description:'+8% research speed per level.' },
    defense_platform: { name:'Defense Platform', maxLevel:10, baseCost:{credits:0,    metal:500,  crystal:150     }, timeBase:120, effects:{baseDPS:50},           icon:'fa-shield-alt',     description:'+50 auto-defense DPS per level.' },
    storage_depot:    { name:'Storage Depot',    maxLevel:15, baseCost:{credits:0,    metal:200                   }, timeBase:60,  effects:{storageBonus:2000},    icon:'fa-warehouse',      description:'+2000 storage cap per level.' },
    sensor_array:     { name:'Sensor Array',     maxLevel:10, baseCost:{credits:200,  metal:300,  crystal:50      }, timeBase:90,  effects:{sensorRange:1},        icon:'fa-broadcast-tower',description:'Reveals sectors; level 7+ enables interdiction.' },
    hangar_bay:       { name:'Hangar Bay',       maxLevel:10, baseCost:{credits:500,  metal:1500, gas:400       }, timeBase:200, effects:{fleetSlots:1},         icon:'fa-shuttle-space',  description:'Level 2/4/7/10 unlock extra fleet slots.' },
    trade_port:       { name:'Trade Port',       maxLevel:10, baseCost:{credits:2500},  timeBase:180, effects:{tradeIncome:5},        icon:'fa-ship',           description:'+5% market income per level.' },
    shield_generator: { name:'Shield Generator', maxLevel:10, baseCost:{credits:4000},  timeBase:240, effects:{shieldDuration:8},     icon:'fa-circle-notch',   description:'Deployable base shield +8hrs per level.' },
    crystal_refinery: { name:'Crystal Refinery', maxLevel:20, baseCost:{credits:900},   timeBase:100, effects:{crystalPerHr:60},      icon:'fa-gem',            description:'+12% crystal/hr per level.' },
  };

  function getBuildingCost(defId, currentLevel) {
    const def = BUILDING_DEFS[defId];
    if (!def) return null;
    const mult = Math.pow(1.6, currentLevel); // exponential cost scaling
    return { credits: Math.floor((def.baseCost.credits || 0) * mult) };
  }

  function getBuildingBuildTime(defId, currentLevel) {
    const def = BUILDING_DEFS[defId];
    if (!def) return 60;
    return Math.floor(def.timeBase * Math.pow(1.8, currentLevel));
  }

  socket.on('get_base_data', async () => {
    try {
      const client = connectedClients.get(socket.id);
      if (!client?.playerData) return socket.emit('base_data', { success: false, error: 'Not authenticated' });
      const pd = client.playerData;
      // Init buildings if missing
      if (!pd.buildings) pd.buildings = { command_center: { level: 1, buildQueue: null } };
      // Check for completed builds
      const now = Date.now();
      let changed = false;
      for (const [id, bld] of Object.entries(pd.buildings)) {
        if (bld.buildQueue && now >= bld.buildQueue.completesAt) {
          bld.level = (bld.level || 1) + 1;
          bld.buildQueue = null;
          changed = true;
          socket.emit('building_upgraded', { success: true, buildingId: id, newLevel: bld.level });
        }
      }
      if (changed) await savePlayerData(pd.userId, pd);
      const buildings = Object.entries(pd.buildings).map(([id, bld]) => {
        const def = BUILDING_DEFS[id] || {};
        const nextCost = getBuildingCost(id, bld.level || 1);
        const nextTime = getBuildingBuildTime(id, bld.level || 1);
        return { id, name: def.name || id, level: bld.level || 1, maxLevel: def.maxLevel || 10,
          icon: def.icon || 'fa-building', description: def.description || '',
          buildQueue: bld.buildQueue || null, nextCost, nextTime, effects: def.effects || {} };
      });
      // Available to build (not yet built)
      const available = Object.entries(BUILDING_DEFS)
        .filter(([id]) => !pd.buildings[id])
        .map(([id, def]) => ({ id, name: def.name, icon: def.icon, description: def.description,
          maxLevel: def.maxLevel, effects: def.effects, cost: getBuildingCost(id, 0), buildTime: getBuildingBuildTime(id, 0) }));
      socket.emit('base_data', { success: true, buildings, available });
    } catch (err) {
      console.error('[BASE] get_base_data error:', err);
      socket.emit('base_data', { success: false, error: err.message });
    }
  });

  socket.on('construct_building', async ({ buildingId }) => {
    try {
      const client = connectedClients.get(socket.id);
      if (!client?.playerData) return;
      const pd = client.playerData;
      if (!pd.buildings) pd.buildings = {};
      if (pd.buildings[buildingId]) return socket.emit('building_constructed', { success: false, error: 'Already built' });
      const def = BUILDING_DEFS[buildingId];
      if (!def) return socket.emit('building_constructed', { success: false, error: 'Unknown building' });
      const cost = getBuildingCost(buildingId, 0);

      // Deduct credits
      if ((pd.stats.credits || 0) < (cost.credits||0)) return socket.emit('building_constructed', { success: false, error: `Need ${cost.credits} credits` });
      pd.stats.credits -= (cost.credits||0);

      // Deduct resources (GDD §6.2)
      resourceSystem.initResources(pd);
      const resCost = {};
      if (cost.metal)    resCost.metal    = cost.metal;
      if (cost.gas)      resCost.gas      = cost.gas;
      if (cost.crystal)  resCost.crystal  = cost.crystal;
      if (Object.keys(resCost).length > 0) {
        try { resourceSystem.spend(pd, resCost); } catch(e) { pd.stats.credits += (cost.credits||0); return socket.emit('building_constructed',{success:false,error:e.message}); }
      }

      const buildTime = getBuildingBuildTime(buildingId, 0) * 1000;
      pd.buildings[buildingId] = { level: 0, buildQueue: { startedAt: Date.now(), completesAt: Date.now() + buildTime } };
      await savePlayerData(pd.userId, pd);
      socket.emit('building_constructed', { success: true, buildingId, completesAt: Date.now() + buildTime });
      socket.emit('economy_data', { credits: pd.stats.credits, gems: pd.stats.gems });
      socket.emit('resource_update',{resources:pd.resources,rates:resourceSystem.getProductionRates(pd),caps:resourceSystem.getStorageCaps(pd)});
    } catch (err) {
      socket.emit('building_constructed', { success: false, error: err.message });
    }
  });

  socket.on('upgrade_building', async ({ buildingId }) => {
    try {
      const client = connectedClients.get(socket.id);
      if (!client?.playerData) return;
      const pd = client.playerData;
      const bld = pd.buildings?.[buildingId];
      if (!bld) return socket.emit('building_upgraded', { success: false, error: 'Building not found' });
      if (bld.buildQueue) return socket.emit('building_upgraded', { success: false, error: 'Already building' });
      const def = BUILDING_DEFS[buildingId];
      if (bld.level >= (def?.maxLevel || 10)) return socket.emit('building_upgraded', { success: false, error: 'Already max level' });
      const cost = getBuildingCost(buildingId, bld.level);

      if ((pd.stats.credits || 0) < (cost.credits||0)) return socket.emit('building_upgraded', { success: false, error: `Need ${cost.credits} credits` });
      pd.stats.credits -= (cost.credits||0);

      resourceSystem.initResources(pd);
      const resCost = {};
      if (cost.metal)   resCost.metal   = cost.metal;
      if (cost.gas)     resCost.gas     = cost.gas;
      if (cost.crystal) resCost.crystal = cost.crystal;
      if (Object.keys(resCost).length > 0) {
        try { resourceSystem.spend(pd, resCost); } catch(e) { pd.stats.credits += (cost.credits||0); return socket.emit('building_upgraded',{success:false,error:e.message}); }
      }

      const buildTime = getBuildingBuildTime(buildingId, bld.level) * 1000;
      bld.buildQueue = { startedAt: Date.now(), completesAt: Date.now() + buildTime };
      await savePlayerData(pd.userId, pd);
      socket.emit('building_upgraded', { success: true, buildingId, completesAt: Date.now() + buildTime });
      socket.emit('economy_data', { credits: pd.stats.credits, gems: pd.stats.gems });
      socket.emit('resource_update',{resources:pd.resources,rates:resourceSystem.getProductionRates(pd),caps:resourceSystem.getStorageCaps(pd)});
    } catch (err) {
      socket.emit('building_upgraded', { success: false, error: err.message });
    }
  });

  socket.on('start_dungeon', (data) => {
    console.log('[GAME SERVER] Starting dungeon for:', socket.id, data);
    try {
      const { dungeonId, userId } = data;
      
      // Check if dungeon is one-time only and already completed
      const dungeon = dungeonSystem.getDungeon(dungeonId);
      if (dungeon && dungeon.oneTimeOnly) {
        const completedDungeons = dungeonSystem.getPlayerCompletedDungeons(userId);
        if (completedDungeons.includes(dungeonId)) {
          socket.emit('dungeon_started', { 
            success: false, 
            error: 'This dungeon can only be completed once per character.' 
          });
          return;
        }
      }
      
      const instance = dungeonSystem.createInstance(dungeonId, userId, []);
      socket.emit('dungeon_started', { instance });
    } catch (error) {
      console.error('[GAME SERVER] Error starting dungeon:', error);
      socket.emit('dungeon_started', { success: false, error: error.message });
    }
  });
  
  socket.on('process_encounter', (data) => {
    console.log('[GAME SERVER] Processing encounter for:', socket.id, data);
    try {
      const { instanceId, userId } = data;
      const result = dungeonSystem.startEncounter(instanceId, userId);
      
      // Auto-complete combat for enemies with 0 attack
      if (result.encounter.enemies && result.encounter.enemies.length > 0) {
        const allZeroAttack = result.encounter.enemies.every(enemy => enemy.attack === 0);
        if (allZeroAttack) {
          console.log('[GAME SERVER] Auto-combat: All enemies have 0 attack, completing encounter');
          const completionResult = dungeonSystem.completeEncounter(instanceId, userId, { victory: true });
          socket.emit('encounter_completed', {
            success: true,
            rewards: completionResult.rewards,
            nextEncounter: completionResult.nextEncounter,
            encounterIndex: completionResult.encounterIndex
          });
          return;
        }
      }
      
      socket.emit('encounter_data', { 
        encounter: result.encounter,
        encounterIndex: result.encounterIndex,
        instance: result.instance
      });
    } catch (error) {
      console.error('[GAME SERVER] Error processing encounter:', error);
      socket.emit('encounter_data', { success: false, error: error.message });
    }
  });
  
  socket.on('complete_dungeon', async (data) => {
    console.log('[GAME SERVER] Completing dungeon for:', socket.id, data);
    try {
      const { instanceId, userId } = data;
      const client = connectedClients.get(socket.id);
      const result = dungeonSystem.completeDungeon(instanceId);

      // Grant item rewards + gem bonuses to player inventory
      if (result.success && client?.userId) {
        try {
          const playerData = await loadPlayerData(client.userId, client.username || 'Player');
          if (playerData) {
            // Grant item rewards
            if (result.rewards?.length) {
              for (const reward of result.rewards) {
                const item = itemSystem.getItem(reward.itemId);
                if (!item) continue;
                itemSystem.applyPurchase(item, playerData, reward.quantity || 1);
              }
            }
            // Gem rewards by dungeon difficulty (GDD §11 v3.2)
            // extreme=1, legendary=3, boss kills add 1 bonus gem
            const gemsByDifficulty = { tutorial:0, easy:0, medium:0, hard:0, extreme:1, legendary:3 };
            const dungeon = dungeonSystem.getDungeon ? dungeonSystem.getDungeon(instanceId) : null;
            const difficulty = dungeon?.difficulty || result.difficulty || 'medium';
            const gemReward = gemsByDifficulty[difficulty] || 0;
            if (gemReward > 0) {
              playerData.stats.gems = (playerData.stats.gems || 0) + gemReward;
              result.gemsAwarded = gemReward;
            }

            await savePlayerData(client.userId, playerData);
            client.playerData = playerData;
            if (gemReward > 0) {
              socket.emit('economy_data', { credits: playerData.stats.credits, gems: playerData.stats.gems });
            }
          }
        } catch (grantErr) {
          console.error('[GAME SERVER] Failed to grant dungeon rewards:', grantErr.message);
        }
      }

      // Log to combat log (GDD §9.5)
      if (client?.playerData) {
        socialSystem.addCombatLogEntry(client.playerData.userId, {
          type: 'dungeon', outcome: 'win',
          enemy: instanceId,
          xpGained: result.xpReward || 0,
          timestamp: new Date()
        }).catch(()=>{});
      }
      socket.emit('dungeon_completed', { rewards: result });
    } catch (error) {
      console.error('[GAME SERVER] Error completing dungeon:', error);
      socket.emit('dungeon_completed', { success: false, error: error.message });
    }
  });
  
  socket.on('get_quests', (data) => {
    console.log('[GAME SERVER] Getting quests for:', socket.id);
    try {
      const userId    = connectedClients.get(socket.id)?.userId || 'anonymous';
      const playerQD  = questSystem.getPlayerQuests(userId);

      // Build quests with per-player progress merged in
      const buildList = (category) =>
        questSystem.getQuestsByCategory(category).map(q => {
          const activeState = playerQD.active[q.id];
          return {
            ...q,
            status: playerQD.completed.includes(q.id)
              ? 'completed'
              : activeState
                ? 'active'
                : (q.prerequisites?.playerLevelMin || 1) <= 1 ? 'available' : 'locked',
            objectives: q.objectives.map(obj => ({
              ...obj,
              progress: activeState?.objectives?.[obj.id]?.progress ?? 0,
              complete:  activeState?.objectives?.[obj.id]?.complete  ?? false,
            })),
          };
        });

      socket.emit('quests_data', {
        success:      true,
        mainQuests:   buildList('main_story'),
        dailyQuests:  buildList('daily'),
        weeklyQuests: buildList('weekly'),
        monthlyQuests:buildList('monthly'),
        playerState:  playerQD,
      });
    } catch (error) {
      console.error('[GAME SERVER] Error getting quests:', error);
      socket.emit('quests_data', { success: false, error: error.message });
    }
  });
  
  // ── quest_completed: a system or action triggers a quest completion ────
  // Note: io.on doesn't exist; use a proper socket-level handler instead.
  // Clients can emit 'complete_quest' to finish a quest; the server validates & pushes result.
  socket.on('complete_quest', async (data) => {
    const { questId } = data || {};
    const client = connectedClients.get(socket.id);
    if (!client?.userId) {
      socket.emit('quest_completed', { success: false, error: 'Not authenticated' });
      return;
    }
    try {
      const quest = questSystem.getQuest(questId);
      if (!quest) {
        socket.emit('quest_completed', { success: false, error: 'Quest not found' });
        return;
      }
      const result = questSystem.completeQuest(client.userId, questId, quest.rewards);
      // Resolve reward amounts into flat numbers for the client
      const rewardCredits = quest.rewards
        ? quest.rewards.filter(r => r.type === 'coin').reduce((s, r) => s + (r.amount || 0), 0)
        : 0;
      const rewardMoney = quest.rewards
        ? quest.rewards.filter(r => r.type === 'money').reduce((s, r) => s + (r.amount || 0), 0)
        : 0;
      // Credit the player + grant item rewards
      if (client.playerData) {
        if (rewardCredits > 0) {
          client.playerData.stats.credits = (client.playerData.stats.credits || 0) + rewardCredits;
        }
        client.playerData.stats.questsCompleted = (client.playerData.stats.questsCompleted || 0) + 1;

        // Grant item rewards (decorations, consumables, etc.)
        const itemRewards = quest.rewards?.filter(r => r.type === 'item') || [];
        for (const reward of itemRewards) {
          const item = itemSystem.getItem(reward.itemId);
          if (item) itemSystem.applyPurchase(item, client.playerData, reward.quantity || 1);
        }

        await savePlayerData(client.playerData.userId, client.playerData);
      }
      socket.emit('quest_completed', {
        success: true,
        questId,
        questName: quest.name,
        rewards: { credits: rewardCredits, money: rewardMoney, items: quest.rewards?.filter(r => r.type === 'item') || [] },
      });
      // Push updated economy to client
      socket.emit('economy_data', {
        credits: client.playerData?.stats?.credits || 0,
        gems:    client.playerData?.stats?.gems    || 0,
      });
    } catch (err) {
      console.error('[GAME SERVER] complete_quest error:', err);
      socket.emit('quest_completed', { success: false, error: err.message });
    }
  });

  
  socket.on('next_room', (data) => {
    console.log('[GAME SERVER] Moving to next room for:', socket.id, data);
    try {
      const { instanceId, userId } = data;
      const result = dungeonSystem.moveToNextRoom(instanceId, userId);
      socket.emit('next_room_data', result);
    } catch (error) {
      console.error('[GAME SERVER] Error moving to next room:', error);
      socket.emit('next_room_data', { success: false, error: error.message });
    }
  });
  
  socket.on('get_dungeon_status', (data) => {
    console.log('[GAME SERVER] Getting dungeon status for:', socket.id, data);
    try {
      const { userId } = data;
      const instance = dungeonSystem.getPlayerInstance(userId);
      if (instance) {
        socket.emit('dungeon_status', { 
          hasActiveDungeon: true, 
          currentInstance: instance 
        });
      } else {
        socket.emit('dungeon_status', { 
          hasActiveDungeon: false, 
          currentInstance: null 
        });
      }
    } catch (error) {
      console.error('[GAME SERVER] Error getting dungeon status:', error);
      socket.emit('dungeon_status', { success: false, error: error.message });
    }
  });
  
  // ── joinServer: client emits after connecting ──────────────────────────
  socket.on('joinServer', (data) => {
    const { serverId, userId, username } = data || {};
    console.log(`[GAME SERVER] joinServer from ${username} (${userId}) for server ${serverId}`);
    // Join a socket.io room named after the serverId so we can broadcast to it
    if (serverId) socket.join(serverId);
    const client = connectedClients.get(socket.id);
    if (client) { client.serverId = serverId; }
    // Notify room that a player joined
    socket.to(serverId).emit('playerJoined', { userId, username });
  });

  // ── getPlayerList ───────────────────────────────────────────────────────
  socket.on('getPlayerList', (data) => {
    const { serverId } = data || {};
    const players = [];
    for (const [sid, cd] of connectedClients) {
      if (cd.serverId === serverId && cd.username) {
        players.push({ userId: cd.userId, username: cd.username });
      }
    }
    socket.emit('playerList', { players });
  });

  // ── chatMessage: relay to everyone in the server room ──────────────────
  socket.on('chatMessage', (data) => {
    const client   = connectedClients.get(socket.id);
    const serverId = client?.serverId;
    const payload  = {
      userId:    client?.userId   || data.userId,
      username:  client?.username || data.username || 'Unknown',
      message:   data.message || '',
      timestamp: Date.now(),
    };
    if (serverId) {
      io.to(serverId).emit('chatMessage', payload);
    } else {
      socket.emit('chatMessage', payload); // echo back to sender only
    }
  });

  // ── updatePlayerStats: client-reported stat delta (server validates) ────
  socket.on('updatePlayerStats', async (data) => {
    const client = connectedClients.get(socket.id);
    if (!client?.playerData) {
      socket.emit('player_stat_update', { success: false, error: 'Not authenticated' });
      return;
    }
    try {
      const pd = client.playerData;
      // Only trust non-economy fields from client; economy is server-authoritative
      const allowed = ['playTime'];
      for (const key of allowed) {
        if (data.playerStats?.[key] !== undefined) {
          pd.stats[key] = data.playerStats[key];
        }
      }
      await savePlayerData(pd.userId, pd);
      socket.emit('player_stat_update', { success: true, stats: pd.stats });
    } catch (err) {
      console.error('[GAME SERVER] updatePlayerStats error:', err);
      socket.emit('player_stat_update', { success: false, error: err.message });
    }
  });

  // ── exit_dungeon: player voluntarily leaves a dungeon ──────────────────
  socket.on('exit_dungeon', (data) => {
    console.log('[GAME SERVER] exit_dungeon for:', socket.id);
    try {
      const { instanceId, userId } = data || {};
      if (instanceId) dungeonSystem.abandonInstance(instanceId);
      socket.emit('dungeon_exited', { success: true });
    } catch (err) {
      console.error('[GAME SERVER] exit_dungeon error:', err);
      socket.emit('dungeon_exited', { success: false, error: err.message });
    }
  });

  // ── get_room_types: dungeon room type definitions ───────────────────────
  socket.on('get_room_types', () => {
    socket.emit('room_types_data', dungeonSystem.roomTypes || {});
  });

  // ════════════════════════════════════════════════════════════════════════
  // SEASON SYSTEM (GDD §20.3)
  // ════════════════════════════════════════════════════════════════════════

  socket.on('get_season', () => {
    const data = seasonSystem.getCurrentSeason();
    if (data.active) {
      const client = connectedClients.get(socket.id);
      if (client?.playerData) data.myScore = seasonSystem.getSeasonScore(client.playerData);
    }
    socket.emit('season_data', data);
  });

  // ════════════════════════════════════════════════════════════════════════
  // GALAXY EVENTS (GDD §20.2)
  // ════════════════════════════════════════════════════════════════════════

  socket.on('get_galaxy_event', () => {
    socket.emit('galaxy_event_data', galaxyEventSystem.getEventData());
  });

  socket.on('claim_event_reward', async () => {
    try {
      const client = connectedClients.get(socket.id);
      if (!client?.playerData) return;
      const result = galaxyEventSystem.claimEventReward(client.playerData);
      if (result.success) await savePlayerData(client.playerData.userId, client.playerData);
      socket.emit('event_reward_result', result);
    } catch(err) { socket.emit('event_reward_result',{success:false,error:err.message}); }
  });

  // ════════════════════════════════════════════════════════════════════════
  // REPUTATION HANDLERS (GDD §15.3)
  // ════════════════════════════════════════════════════════════════════════

  socket.on('get_reputation', async () => {
    try {
      const client = connectedClients.get(socket.id);
      if (!client?.playerData) return;
      reputationSystem.initReputation(client.playerData);
      socket.emit('reputation_data', { success:true, reputations: reputationSystem.getReputationData(client.playerData) });
    } catch(err) { socket.emit('reputation_data',{success:false,error:err.message}); }
  });

  // ════════════════════════════════════════════════════════════════════════
  // SOCIAL: FRIENDS + COMBAT LOG (GDD §17.2, §9.5)
  // ════════════════════════════════════════════════════════════════════════

  socket.on('get_friends', async () => {
    try {
      const client = connectedClients.get(socket.id);
      if (!client?.playerData) return;
      const data = await socialSystem.getFriendsList(client.playerData.userId, client.playerData.username, connectedClients);
      socket.emit('friends_data', { success:true, ...data });
    } catch(err) { socket.emit('friends_data',{success:false,error:err.message}); }
  });

  socket.on('add_friend', async ({ username }={}) => {
    try {
      const client = connectedClients.get(socket.id);
      if (!client?.playerData) return;
      const result = await socialSystem.sendFriendRequest(client.playerData.userId, client.playerData.username, username, connectedClients);
      socket.emit('friend_request_sent', { success:true, targetName: result.targetName });
      // Notify target if online
      for (const [sid, cd] of connectedClients.entries()) {
        if (cd.userId === result.targetId) io.to(sid).emit('friend_request', { fromId: client.playerData.userId, fromName: client.playerData.username });
      }
    } catch(err) { socket.emit('friend_request_sent',{success:false,error:err.message}); }
  });

  socket.on('accept_friend', async ({ fromId, fromName }={}) => {
    try {
      const client = connectedClients.get(socket.id);
      if (!client?.playerData) return;
      await socialSystem.acceptFriendRequest(client.playerData.userId, client.playerData.username, fromId, fromName);
      socket.emit('friend_accepted', { success:true, friendId: fromId, friendName: fromName });
      for (const [sid, cd] of connectedClients.entries()) {
        if (cd.userId === fromId) io.to(sid).emit('friend_accepted', { friendId: client.playerData.userId, friendName: client.playerData.username });
      }
    } catch(err) { socket.emit('friend_accepted',{success:false,error:err.message}); }
  });

  socket.on('remove_friend', async ({ friendId }={}) => {
    try {
      const client = connectedClients.get(socket.id);
      if (!client?.playerData) return;
      await socialSystem.removeFriend(client.playerData.userId, friendId);
      socket.emit('friend_removed', { success:true });
    } catch(err) { socket.emit('friend_removed',{success:false,error:err.message}); }
  });

  socket.on('send_gift', async ({ targetId, amount }={}) => {
    try {
      const client = connectedClients.get(socket.id);
      if (!client?.playerData) return;
      const result = await socialSystem.sendGift(client.playerData, targetId, amount);
      await savePlayerData(client.playerData.userId, client.playerData);
      socket.emit('gift_sent', { success:true, amount });
      socket.emit('economy_data',{credits:client.playerData.stats.credits,gems:client.playerData.stats.gems});
      // Give credits to recipient if online
      for (const [sid, cd] of connectedClients.entries()) {
        if (cd.userId === targetId) {
          cd.playerData.stats.credits = (cd.playerData.stats.credits||0) + amount;
          io.to(sid).emit('gift_received', { fromName: client.playerData.username, amount });
          io.to(sid).emit('economy_data',{credits:cd.playerData.stats.credits,gems:cd.playerData.stats.gems});
          await savePlayerData(cd.userId, cd.playerData);
        }
      }
    } catch(err) { socket.emit('gift_sent',{success:false,error:err.message}); }
  });

  socket.on('get_combat_log', async () => {
    try {
      const client = connectedClients.get(socket.id);
      if (!client?.playerData) return;
      const log = await socialSystem.getCombatLog(client.playerData.userId);
      socket.emit('combat_log_data', { success:true, log });
    } catch(err) { socket.emit('combat_log_data',{success:false,error:err.message}); }
  });

  // ════════════════════════════════════════════════════════════════════════
  // PLAYER MARKET HANDLERS (GDD §14)
  // ════════════════════════════════════════════════════════════════════════

  socket.on('get_market', async ({ itemId, category } = {}) => {
    try {
      const listings = await marketSystem.getListings({ itemId, category });
      const tradeRes = marketSystem.getTradeableResources();
      socket.emit('market_data', { success:true, listings, tradeableResources: tradeRes });
    } catch(err) { socket.emit('market_data',{success:false,error:err.message}); }
  });

  socket.on('get_my_listings', async () => {
    try {
      const client = connectedClients.get(socket.id);
      if (!client?.playerData) return;
      const listings = await marketSystem.getMyListings(client.playerData.userId);
      socket.emit('my_listings', { success:true, listings });
    } catch(err) { socket.emit('my_listings',{success:false,error:err.message}); }
  });

  socket.on('list_resource', async (data={}) => {
    try {
      const client = connectedClients.get(socket.id);
      if (!client?.playerData) return;
      resourceSystem.initResources(client.playerData);
      const result = await marketSystem.listResource(client.playerData, data);
      await savePlayerData(client.playerData.userId, client.playerData);
      socket.emit('list_result', { success:true, listing:result.listing, listingFee:result.listingFee });
      socket.emit('resource_update',{resources:client.playerData.resources,rates:resourceSystem.getProductionRates(client.playerData),caps:resourceSystem.getStorageCaps(client.playerData)});
    } catch(err) { socket.emit('list_result',{success:false,error:err.message}); }
  });

  socket.on('list_item', async (data={}) => {
    try {
      const client = connectedClients.get(socket.id);
      if (!client?.playerData) return;
      const result = await marketSystem.listItem(client.playerData, data);
      await savePlayerData(client.playerData.userId, client.playerData);
      socket.emit('list_result', { success:true, listing:result.listing, listingFee:result.listingFee });
    } catch(err) { socket.emit('list_result',{success:false,error:err.message}); }
  });

  socket.on('buy_listing', async ({ listingId }={}) => {
    try {
      const client = connectedClients.get(socket.id);
      if (!client?.playerData) return;
      const result = await marketSystem.buyListing(client.playerData, listingId);

      // Credit seller — online: immediate push; offline: persist to DB so they receive on next login
      let sellerCredited = false;
      for (const [sid, cd] of connectedClients.entries()) {
        if (cd.userId === result.listing.sellerId) {
          cd.playerData.stats.credits = (cd.playerData.stats.credits||0) + result.proceeds;
          io.to(sid).emit('economy_data',{credits:cd.playerData.stats.credits,gems:cd.playerData.stats.gems});
          io.to(sid).emit('market_sale', { listingId, itemName: result.listing.itemName, amount: result.proceeds });
          await savePlayerData(cd.userId, cd.playerData);
          sellerCredited = true;
        }
      }
      // Offline seller: load their data from DB, credit, and resave
      if (!sellerCredited) {
        try {
          const PlayerData = require('./models/PlayerData');
          const sellerDoc = await PlayerData.findOne({ userId: result.listing.sellerId });
          if (sellerDoc) {
            sellerDoc.stats.credits = (sellerDoc.stats.credits || 0) + result.proceeds;
            await sellerDoc.save();
          }
        } catch(e) { console.warn('[market] offline seller credit error:', e.message); }
      }

      await savePlayerData(client.playerData.userId, client.playerData);
      socket.emit('buy_result',{success:true,listingId,proceeds:result.proceeds,itemName:result.listing.itemName});
      socket.emit('economy_data',{credits:client.playerData.stats.credits,gems:client.playerData.stats.gems});
    } catch(err) { socket.emit('buy_result',{success:false,error:err.message}); }
  });

  // Price history for market sparklines (GDD §14 v3.2)
  socket.on('get_price_history', async ({ itemId, days = 14 } = {}) => {
    try {
      if (!itemId) return socket.emit('price_history', { error: 'itemId required' });
      const history = await marketSystem.getPriceHistory(itemId, Math.min(days, 30));
      socket.emit('price_history', history);
    } catch(err) { socket.emit('price_history', { error: err.message }); }
  });

  socket.on('cancel_listing', async ({ listingId }={}) => {
    try {
      const client = connectedClients.get(socket.id);
      if (!client?.playerData) return;
      await marketSystem.cancelListing(client.playerData, listingId);
      await savePlayerData(client.playerData.userId, client.playerData);
      socket.emit('cancel_listing_result',{success:true});
    } catch(err) { socket.emit('cancel_listing_result',{success:false,error:err.message}); }
  });

  // ════════════════════════════════════════════════════════════════════════
  // ALLIANCE HANDLERS (GDD §12)
  // ════════════════════════════════════════════════════════════════════════

  socket.on('get_alliance', async () => {
    try {
      const client = connectedClients.get(socket.id);
      if (!client?.playerData) return;
      const pd = client.playerData;
      const allianceData = pd.allianceId ? await allianceSystem.getAllianceData(pd.allianceId) : null;
      socket.emit('alliance_data', { success:true, alliance:allianceData, playerRank: pd.allianceRank, playerAllianceId: pd.allianceId });
    } catch(err) { socket.emit('alliance_data',{success:false,error:err.message}); }
  });

  socket.on('search_alliances', async ({ query }={}) => {
    try {
      const results = await allianceSystem.searchAlliances(query);
      socket.emit('alliance_search_results', { success:true, results });
    } catch(err) { socket.emit('alliance_search_results',{success:false,error:err.message}); }
  });

  socket.on('create_alliance', async ({ name, tag, description }={}) => {
    try {
      const client = connectedClients.get(socket.id);
      if (!client?.playerData) return;
      const alliance = await allianceSystem.createAlliance(client.playerData, { name, tag, description });
      await savePlayerData(client.playerData.userId, client.playerData);
      socket.emit('alliance_created', { success:true, alliance });
      socket.emit('economy_data', { credits: client.playerData.stats.credits, gems: client.playerData.stats.gems });
    } catch(err) { socket.emit('alliance_created',{success:false,error:err.message}); }
  });

  socket.on('join_alliance', async ({ allianceId }={}) => {
    try {
      const client = connectedClients.get(socket.id);
      if (!client?.playerData) return;
      const alliance = await allianceSystem.joinAlliance(client.playerData, allianceId);
      await savePlayerData(client.playerData.userId, client.playerData);
      socket.emit('alliance_joined', { success:true, alliance });
    } catch(err) { socket.emit('alliance_joined',{success:false,error:err.message}); }
  });

  socket.on('leave_alliance', async () => {
    try {
      const client = connectedClients.get(socket.id);
      if (!client?.playerData) return;
      await allianceSystem.leaveAlliance(client.playerData);
      await savePlayerData(client.playerData.userId, client.playerData);
      socket.emit('alliance_left', { success:true });
    } catch(err) { socket.emit('alliance_left',{success:false,error:err.message}); }
  });

  socket.on('alliance_deposit', async (data={}) => {
    try {
      const client = connectedClients.get(socket.id);
      if (!client?.playerData) return;
      const warehouse = await allianceSystem.depositWarehouse(client.playerData, data);
      await savePlayerData(client.playerData.userId, client.playerData);
      socket.emit('alliance_warehouse_update', { success:true, warehouse });
      socket.emit('resource_update',{resources:client.playerData.resources,rates:resourceSystem.getProductionRates(client.playerData),caps:resourceSystem.getStorageCaps(client.playerData)});
    } catch(err) { socket.emit('alliance_warehouse_update',{success:false,error:err.message}); }
  });

  socket.on('alliance_withdraw', async (data={}) => {
    try {
      const client = connectedClients.get(socket.id);
      if (!client?.playerData) return;
      const warehouse = await allianceSystem.withdrawWarehouse(client.playerData, data);
      await savePlayerData(client.playerData.userId, client.playerData);
      socket.emit('alliance_warehouse_update', { success:true, warehouse });
    } catch(err) { socket.emit('alliance_warehouse_update',{success:false,error:err.message}); }
  });

  // Alliance Research Tree (GDD §12.3 — v3.2)
  socket.on('get_alliance_research', async () => {
    try {
      const client = connectedClients.get(socket.id);
      if (!client?.playerData?.allianceId) return socket.emit('alliance_research_data', { error: 'Not in an alliance' });
      const Alliance = require('./systems/AllianceSystem').Alliance || mongoose.model('Alliance');
      const alliance = await Alliance.findOne({ allianceId: client.playerData.allianceId });
      if (!alliance) return socket.emit('alliance_research_data', { error: 'Alliance not found' });
      const tree = require('./data/gso/alliance/research_tree.json');
      socket.emit('alliance_research_data', {
        tree,
        completed: alliance.research?.completed || [],
        inProgress: alliance.research?.inProgress || null,
      });
    } catch(err) { socket.emit('alliance_research_data', { error: err.message }); }
  });

  socket.on('start_alliance_research', async ({ techId } = {}) => {
    try {
      const client = connectedClients.get(socket.id);
      if (!client?.playerData) return;
      if (!client.playerData.allianceId) return socket.emit('alliance_research_result', { success:false, error:'Not in an alliance' });
      if (!['founder','officer'].includes(client.playerData.allianceRank))
        return socket.emit('alliance_research_result', { success:false, error:'Only founders and officers can start research' });

      const Alliance = mongoose.model('Alliance');
      const alliance = await Alliance.findOne({ allianceId: client.playerData.allianceId });
      if (!alliance) return socket.emit('alliance_research_result', { success:false, error:'Alliance not found' });

      const tree = require('./data/gso/alliance/research_tree.json');
      const tech = tree.tiers.flatMap(t => t.techs).find(t => t.id === techId);
      if (!tech) return socket.emit('alliance_research_result', { success:false, error:'Tech not found' });

      const completed = alliance.research?.completed || [];
      // Check prerequisites
      for (const prereq of (tech.prereq || [])) {
        if (!completed.includes(prereq))
          return socket.emit('alliance_research_result', { success:false, error:`Requires ${prereq} first` });
      }
      if (completed.includes(techId))
        return socket.emit('alliance_research_result', { success:false, error:'Already researched' });
      if (alliance.research?.inProgress)
        return socket.emit('alliance_research_result', { success:false, error:'Research already in progress' });

      // Deduct cost from warehouse
      const cost = tech.cost || {};
      const wh   = alliance.warehouse;
      for (const [res, amt] of Object.entries(cost)) {
        if (res === 'credits') {
          if ((wh.credits||0) < amt) return socket.emit('alliance_research_result', {success:false, error:`Need ${amt} warehouse credits`});
          wh.credits -= amt;
        } else {
          if ((wh[res]||0) < amt) return socket.emit('alliance_research_result', {success:false, error:`Need ${amt} warehouse ${res}`});
          wh[res] -= amt;
        }
      }

      const researchTimeSec = 3600; // 1 hour per tech (Phase 3 may vary)
      alliance.research = {
        ...(alliance.research||{}),
        completed,
        inProgress: { techId, startedAt: Date.now(), completesAt: Date.now() + researchTimeSec * 1000 },
      };
      alliance.markModified('research');
      alliance.markModified('warehouse');
      await alliance.save();

      socket.emit('alliance_research_result', {
        success: true, techId,
        completesAt: alliance.research.inProgress.completesAt,
        warehouse: alliance.warehouse,
      });
    } catch(err) { socket.emit('alliance_research_result', {success:false, error:err.message}); }
  });

  socket.on('collect_alliance_research', async () => {
    try {
      const client = connectedClients.get(socket.id);
      if (!client?.playerData?.allianceId) return;
      const Alliance = mongoose.model('Alliance');
      const alliance = await Alliance.findOne({ allianceId: client.playerData.allianceId });
      if (!alliance?.research?.inProgress) return socket.emit('alliance_research_result', {success:false, error:'No research in progress'});
      if (Date.now() < alliance.research.inProgress.completesAt)
        return socket.emit('alliance_research_result', {success:false, error:'Not complete yet', remainingMs: alliance.research.inProgress.completesAt - Date.now()});

      const techId = alliance.research.inProgress.techId;
      alliance.research.completed = [...(alliance.research.completed||[]), techId];
      alliance.research.inProgress = null;
      alliance.markModified('research');
      await alliance.save();
      socket.emit('alliance_research_result', { success:true, completed: techId, allCompleted: alliance.research.completed });
    } catch(err) { socket.emit('alliance_research_result', {success:false, error:err.message}); }
  });

  // ════════════════════════════════════════════════════════════════════════
  // FLEET MISSION HANDLERS (GDD §8.3)
  // ════════════════════════════════════════════════════════════════════════

  socket.on('get_missions', async () => {
    try {
      const client = connectedClients.get(socket.id);
      if (!client?.playerData) return;
      const pd = client.playerData;
      // Auto-complete any finished missions
      const results = missionSystem.collectMissions(pd, resourceSystem);
      if (results.length > 0) {
        await savePlayerData(pd.userId, pd);
        for (const r of results) socket.emit('mission_completed', r);
      }
      socket.emit('missions_data', missionSystem.getMissionsForPlayer(pd));
    } catch(err) { console.error('[MISSIONS]', err); }
  });

  socket.on('start_mission', async ({ missionType, fleetShipIds } = {}) => {
    try {
      const client = connectedClients.get(socket.id);
      if (!client?.playerData) return;
      const mission = missionSystem.startMission(client.playerData, { missionType, fleetShipIds });
      await savePlayerData(client.playerData.userId, client.playerData);
      socket.emit('mission_started', { success: true, mission });
      socket.emit('missions_data', missionSystem.getMissionsForPlayer(client.playerData));
    } catch(err) {
      socket.emit('mission_started', { success: false, error: err.message });
    }
  });

  socket.on('collect_missions', async () => {
    try {
      const client = connectedClients.get(socket.id);
      if (!client?.playerData) return;
      const pd = client.playerData;
      const results = missionSystem.collectMissions(pd, resourceSystem);
      await savePlayerData(pd.userId, pd);
      socket.emit('missions_collected', { results, missions: missionSystem.getMissionsForPlayer(pd) });
      if (results.some(r => r.rewards?.metal || r.rewards?.gas)) {
        socket.emit('resource_update', { resources:pd.resources, rates:resourceSystem.getProductionRates(pd), caps:resourceSystem.getStorageCaps(pd) });
      }
    } catch(err) { console.error('[COLLECT_MISSIONS]', err); }
  });

  socket.on('get_faction_missions', async () => {
    try {
      const client = connectedClients.get(socket.id);
      if (!client?.playerData) return;
      const pd = client.playerData;
      // Auto-collect completed faction missions first
      const results = missionSystem.collectFactionMissions(pd, resourceSystem, reputationSystem);
      if (results.length > 0) {
        await savePlayerData(pd.userId, pd);
        results.forEach(r => socket.emit('mission_completed', r));
      }
      socket.emit('faction_missions_data', {
        success: true,
        available: missionSystem.getAvailableFactionMissions(pd),
        active: (pd.fleetMissions||[]).filter(m => m.type==='faction'),
      });
    } catch(err) { socket.emit('faction_missions_data',{success:false,error:err.message}); }
  });

  socket.on('start_faction_mission', async ({ missionId }={}) => {
    try {
      const client = connectedClients.get(socket.id);
      if (!client?.playerData) return;
      const job = missionSystem.startFactionMission(client.playerData, missionId);
      await savePlayerData(client.playerData.userId, client.playerData);
      socket.emit('faction_mission_started', { success:true, job });
    } catch(err) { socket.emit('faction_mission_started',{success:false,error:err.message}); }
  });

  socket.on('recall_mission', async ({ missionId } = {}) => {
    try {
      const client = connectedClients.get(socket.id);
      if (!client?.playerData) return;
      missionSystem.recallMission(client.playerData, missionId);
      await savePlayerData(client.playerData.userId, client.playerData);
      socket.emit('missions_data', missionSystem.getMissionsForPlayer(client.playerData));
    } catch(err) { socket.emit('recall_mission_result', { success:false, error:err.message }); }
  });

  // ════════════════════════════════════════════════════════════════════════
  // RESOURCE HANDLERS (GDD §5)
  // ════════════════════════════════════════════════════════════════════════

  socket.on('get_resources', async () => {
    try {
      const client = connectedClients.get(socket.id);
      if (!client?.playerData) return;
      resourceSystem.initResources(client.playerData);
      // Run a tick to credit any offline production
      resourceSystem.tick(client.playerData);
      await savePlayerData(client.playerData.userId, client.playerData);
      socket.emit('resource_update', {
        resources: client.playerData.resources,
        rates:     resourceSystem.getProductionRates(client.playerData),
        caps:      resourceSystem.getStorageCaps(client.playerData),
        config:    resourceSystem.getConfig(),
      });
    } catch (err) {
      console.error('[RESOURCES] get_resources error:', err);
    }
  });

  // ════════════════════════════════════════════════════════════════════════
  // SHIP CONSTRUCTION QUEUE (GDD §7.4)
  // ════════════════════════════════════════════════════════════════════════

  const SHIP_BLUEPRINTS = {
    starter_cruiser:  { name:'Starter Cruiser', icon:'🚀', rarity:'common',   hull:100, attack:10, defense:5,  speed:15, buildTime:30,   metalCost:200,  gasCost:50,   crystalCost:0,   level:1  },
    light_fighter:    { name:'Light Fighter',   icon:'✈',  rarity:'uncommon', hull:80,  attack:18, defense:3,  speed:25, buildTime:60,   metalCost:400,  gasCost:150,  crystalCost:50,  level:3  },
    heavy_bomber:     { name:'Heavy Bomber',    icon:'💣', rarity:'uncommon', hull:160, attack:25, defense:8,  speed:10, buildTime:120,  metalCost:800,  gasCost:200,  crystalCost:100, level:5  },
    destroyer:        { name:'Destroyer',       icon:'⚔',  rarity:'rare',    hull:200, attack:30, defense:15, speed:18, buildTime:300,  metalCost:1500, gasCost:500,  crystalCost:300, level:8  },
    heavy_cruiser:    { name:'Heavy Cruiser',   icon:'🛸', rarity:'rare',    hull:350, attack:40, defense:25, speed:12, buildTime:600,  metalCost:3000, gasCost:1000, crystalCost:800, level:12 },
    battleship:       { name:'Battleship',      icon:'🌟', rarity:'epic',    hull:600, attack:60, defense:45, speed:8,  buildTime:1800, metalCost:8000, gasCost:3000, crystalCost:2000,level:20 },
  };

  function getShipyardLevel(playerData) {
    return playerData.buildings?.shipyard?.level || 0;
  }
  function getShipQueueMax(playerData) {
    const sy = getShipyardLevel(playerData);
    return sy >= 1 ? Math.min(3, 1 + Math.floor(sy / 3)) : 0;
  }
  function checkShipQueue(playerData) {
    const q = playerData.shipQueue || [];
    const now = Date.now();
    let changed = false;
    const completed = [];
    const remaining = [];
    for (const job of q) {
      if (job.completesAt <= now) {
        // Add ship to inventory
        const bp = SHIP_BLUEPRINTS[job.shipId];
        if (bp) {
          if (!playerData.inventory) playerData.inventory = [];
          playerData.inventory.push({
            id: job.shipId + '_' + Date.now(),
            type: 'ship', shipId: job.shipId,
            name: bp.name, icon: bp.icon, rarity: bp.rarity,
            stats: { hull: bp.hull, maxHull: bp.hull, attack: bp.attack, defense: bp.defense, speed: bp.speed, currentHull: bp.hull }
          });
          completed.push({ shipId: job.shipId, name: bp.name });
          changed = true;
        }
      } else {
        remaining.push(job);
      }
    }
    playerData.shipQueue = remaining;
    return { changed, completed };
  }

  socket.on('get_shipyard', async () => {
    try {
      const client = connectedClients.get(socket.id);
      if (!client?.playerData) return;
      const pd = client.playerData;
      checkShipQueue(pd);
      const ships = (pd.inventory || []).filter(i => i.type === 'ship');
      socket.emit('shipyard_data', {
        success: true,
        blueprints: SHIP_BLUEPRINTS,
        queue: pd.shipQueue || [],
        ships,
        shipyardLevel: getShipyardLevel(pd),
        queueMax: getShipQueueMax(pd),
        activeShipId: pd.stats?.activeShipId,
        resources: pd.resources,
      });
    } catch(err) { console.error('[SHIPYARD]', err); }
  });

  socket.on('build_ship', async ({ shipId } = {}) => {
    try {
      const client = connectedClients.get(socket.id);
      if (!client?.playerData) return;
      const pd = client.playerData;
      const bp = SHIP_BLUEPRINTS[shipId];
      if (!bp) return socket.emit('build_ship_result', { success:false, error:'Unknown blueprint' });

      const syLevel = getShipyardLevel(pd);
      if (syLevel < 1) return socket.emit('build_ship_result', { success:false, error:'Build a Shipyard first (Base → Buildings)' });
      if ((pd.stats?.level || 1) < bp.level) return socket.emit('build_ship_result', { success:false, error:`Requires Commander Level ${bp.level}` });

      const q = pd.shipQueue || [];
      const qMax = getShipQueueMax(pd);
      if (q.length >= qMax) return socket.emit('build_ship_result', { success:false, error:`Queue full (${qMax} slots). Upgrade Shipyard for more.` });

      // Deduct resources
      resourceSystem.initResources(pd);
      try {
        resourceSystem.spend(pd, { metal: bp.metalCost, gas: bp.gasCost, crystal: bp.crystalCost });
      } catch(e) { return socket.emit('build_ship_result', { success:false, error: e.message }); }

      const now = Date.now();
      const job = { shipId, name:bp.name, icon:bp.icon, startedAt:now, completesAt: now + bp.buildTime*1000 };
      pd.shipQueue = [...q, job];
      await savePlayerData(pd.userId, pd);

      socket.emit('build_ship_result', { success:true, job, resources:pd.resources });
      socket.emit('resource_update', { resources:pd.resources, rates:resourceSystem.getProductionRates(pd), caps:resourceSystem.getStorageCaps(pd) });
    } catch(err) { console.error('[BUILD_SHIP]', err); socket.emit('build_ship_result',{success:false,error:'Server error'}); }
  });

  socket.on('cancel_ship_build', async ({ shipId, startedAt } = {}) => {
    try {
      const client = connectedClients.get(socket.id);
      if (!client?.playerData) return;
      const pd = client.playerData;
      const idx = (pd.shipQueue||[]).findIndex(j => j.shipId===shipId && j.startedAt===startedAt);
      if (idx < 0) return socket.emit('cancel_ship_result',{success:false,error:'Job not found'});
      const job = pd.shipQueue[idx];
      const bp = SHIP_BLUEPRINTS[job.shipId];
      // Refund 75%
      if (bp) resourceSystem.add(pd, { metal:Math.floor(bp.metalCost*.75), gas:Math.floor(bp.gasCost*.75), crystal:Math.floor(bp.crystalCost*.75) });
      pd.shipQueue.splice(idx,1);
      await savePlayerData(pd.userId, pd);
      socket.emit('cancel_ship_result',{success:true});
      socket.emit('resource_update',{resources:pd.resources,rates:resourceSystem.getProductionRates(pd),caps:resourceSystem.getStorageCaps(pd)});
    } catch(err) { console.error('[CANCEL_SHIP]',err); }
  });

  // ════════════════════════════════════════════════════════════════════════
  // LEADERBOARD HANDLER
  // ════════════════════════════════════════════════════════════════════════

  socket.on('get_leaderboard', async ({ category = 'level' } = {}) => {
    try {
      const VALID_CATEGORIES = ['level', 'credits', 'questsCompleted', 'dungeonsCleared', 'totalKills'];
      if (!VALID_CATEGORIES.includes(category)) {
        return socket.emit('leaderboard_data', { success: false, error: 'Invalid category' });
      }

      // Query top 20 players for the requested stat
      const sortField = `stats.${category}`;
      const players = await PlayerData.find(
        { [`stats.${category}`]: { $exists: true, $gt: 0 } },
        { 'stats.username': 1, username: 1, [`stats.${category}`]: 1, 'stats.level': 1, updatedAt: 1 }
      )
        .sort({ [sortField]: -1 })
        .limit(20)
        .lean();

      const myUserId = connectedClients.get(socket.id)?.userId;
      const entries = players.map((p, i) => ({
        rank: i + 1,
        username: p.username || p.stats?.username || 'Unknown Commander',
        value: p.stats?.[category] || 0,
        level: p.stats?.level || 1,
        isMe: p.userId === myUserId || p._id?.toString() === myUserId,
      }));

      socket.emit('leaderboard_data', { success: true, category, entries });
    } catch (err) {
      console.error('[LEADERBOARD] get_leaderboard error:', err);
      socket.emit('leaderboard_data', { success: false, error: err.message });
    }
  });

  // ════════════════════════════════════════════════════════════════════════
  // FLEET HANDLERS
  // ════════════════════════════════════════════════════════════════════════

  socket.on('get_fleet_data', async () => {
    try {
      const client = connectedClients.get(socket.id);
      if (!client?.playerData) return socket.emit('fleet_data', { success: false, error: 'Not authenticated' });
      // Auto-give starter ship if inventory has no ships
      const ships = (client.playerData.inventory || []).filter(i => i.type === 'ship');
      if (ships.length === 0) {
        const starterShip = {
          id: `ship_starter_${Date.now()}`,
          name: 'Starter Cruiser',
          type: 'ship',
          rarity: 'common',
          texture: 'assets/gso/textures/ships/starter_cruiser_common.png',
          stats: { attack: 10, defense: 5, speed: 10, hull: 100, maxHull: 100, currentHull: 100 },
          level: 1, equipped: false,
        };
        client.playerData.inventory = client.playerData.inventory || [];
        client.playerData.inventory.push(starterShip);
        client.playerData.stats.activeShipId = starterShip.id;
        await savePlayerData(client.playerData.userId, client.playerData);
      }
      const fleetData = fleetSystem.getFleetData(client.playerData);
      const templates = fleetSystem.getAllShipTemplates();
      socket.emit('fleet_data', { success: true, ...fleetData, templates });
    } catch (err) {
      console.error('[FLEET] get_fleet_data error:', err);
      socket.emit('fleet_data', { success: false, error: err.message });
    }
  });

  socket.on('set_active_ship', async ({ shipId }) => {
    try {
      const client = connectedClients.get(socket.id);
      if (!client?.playerData) return;
      const stats = fleetSystem.setActiveShip(client.playerData, shipId);
      await savePlayerData(client.playerData.userId, client.playerData);
      socket.emit('active_ship_set', { success: true, shipId, stats });
    } catch (err) {
      socket.emit('active_ship_set', { success: false, error: err.message });
    }
  });

  socket.on('repair_ship', async ({ shipId }) => {
    try {
      const client = connectedClients.get(socket.id);
      if (!client?.playerData) return;
      const credits = client.playerData.stats?.credits || 0;
      const result = fleetSystem.repairShip(client.playerData, shipId, credits);
      client.playerData.stats.credits -= result.repairCost;
      await savePlayerData(client.playerData.userId, client.playerData);
      socket.emit('ship_repaired', { success: true, shipId, repairCost: result.repairCost });
      socket.emit('economy_data', { credits: client.playerData.stats.credits, gems: client.playerData.stats.gems });
    } catch (err) {
      socket.emit('ship_repaired', { success: false, error: err.message });
    }
  });

  // ════════════════════════════════════════════════════════════════════════
  // GALAXY MAP HANDLERS
  // ════════════════════════════════════════════════════════════════════════

  socket.on('get_galaxy_map', async () => {
    try {
      const client = connectedClients.get(socket.id);
      if (!client?.playerData) return;
      // Seed home sector if first time
      if (!client.playerData.homeSector) {
        client.playerData.homeSector = '15_10';
        client.playerData.exploredSectors = ['15_10'];
        await savePlayerData(client.playerData.userId, client.playerData);
      }
      const visibleSectors = galaxySystem.getVisibleSectors(client.playerData);
      socket.emit('galaxy_map_data', {
        success: true,
        sectors: visibleSectors,
        homeSector: client.playerData.homeSector,
        gridW: galaxySystem.GRID_W,
        gridH: galaxySystem.GRID_H,
      });
    } catch (err) {
      console.error('[GALAXY] get_galaxy_map error:', err);
      socket.emit('galaxy_map_data', { success: false, error: err.message });
    }
  });

  socket.on('explore_sector', async ({ sectorId }) => {
    try {
      const client = connectedClients.get(socket.id);
      if (!client?.playerData) return;
      const sector = galaxySystem.exploreSector(sectorId, client.playerData);
      await savePlayerData(client.playerData.userId, client.playerData);
      // XP reward for first explore
      const xpGain = 50 + sector.threat * 10;
      client.playerData.stats.experience = (client.playerData.stats.experience || 0) + xpGain;
      socket.emit('sector_explored', { success: true, sector, xpGain });
      // Push updated visible sectors
      const visibleSectors = galaxySystem.getVisibleSectors(client.playerData);
      socket.emit('galaxy_map_data', {
        success: true,
        sectors: visibleSectors,
        homeSector: client.playerData.homeSector,
        gridW: galaxySystem.GRID_W,
        gridH: galaxySystem.GRID_H,
      });
    } catch (err) {
      socket.emit('sector_explored', { success: false, error: err.message });
    }
  });

  // ════════════════════════════════════════════════════════════════════════
  // RESEARCH HANDLERS
  // ════════════════════════════════════════════════════════════════════════

  socket.on('get_research_data', async () => {
    try {
      const client = connectedClients.get(socket.id);
      if (!client?.playerData) return;
      // Check for completed research first
      const justCompleted = researchSystem.checkCompletion(client.playerData);
      if (justCompleted) {
        await savePlayerData(client.playerData.userId, client.playerData);
        socket.emit('research_completed', { tech: justCompleted });
      }
      const available = researchSystem.getAvailableResearch(client.playerData);
      socket.emit('research_data', {
        success: true,
        research: available,
        inProgress: client.playerData.research?.inProgress || null,
        completed: client.playerData.research?.completed || [],
        effects: client.playerData.research?.effects || {},
      });
    } catch (err) {
      console.error('[RESEARCH] get_research_data error:', err);
      socket.emit('research_data', { success: false, error: err.message });
    }
  });

  socket.on('start_research', async ({ techId }) => {
    try {
      const client = connectedClients.get(socket.id);
      if (!client?.playerData) return;
      const result = researchSystem.startResearch(client.playerData, techId);
      await savePlayerData(client.playerData.userId, client.playerData);
      socket.emit('research_started', { success: true, tech: result.tech, completesAt: result.completesAt });
      socket.emit('economy_data', { credits: client.playerData.stats.credits, gems: client.playerData.stats.gems });
    } catch (err) {
      socket.emit('research_started', { success: false, error: err.message });
    }
  });

  socket.on('cancel_research', async () => {
    try {
      const client = connectedClients.get(socket.id);
      if (!client?.playerData) return;
      const tech = researchSystem.cancelResearch(client.playerData);
      await savePlayerData(client.playerData.userId, client.playerData);
      socket.emit('research_cancelled', { success: true, tech });
      socket.emit('economy_data', { credits: client.playerData.stats.credits, gems: client.playerData.stats.gems });
    } catch (err) {
      socket.emit('research_cancelled', { success: false, error: err.message });
    }
  });

  // ════════════════════════════════════════════════════════════════════════
  // SHIP MODULE SYSTEM (GDD §7.3)
  // ════════════════════════════════════════════════════════════════════════

  // Get equipped modules for active ship
  socket.on('get_ship_modules', async () => {
    if (!socket.userId) return;
    try {
      const pd = await PlayerData.findOne({ userId: socket.userId });
      if (!pd) return;
      const ship = pd.ship || {};
      socket.emit('ship_modules_data', {
        shipId: ship.id || ship.shipId || null,
        modules: ship.modules || {},
        availableSlots: _getShipSlots(ship),
      });
    } catch (err) {
      console.error('[MODULES] get_ship_modules error:', err);
    }
  });

  // Equip a module item into a slot
  socket.on('equip_module', async ({ itemId, slot } = {}) => {
    if (!socket.userId) return socket.emit('equip_result', { success: false, error: 'Not authenticated' });
    if (!itemId || !slot) return socket.emit('equip_result', { success: false, error: 'Missing itemId or slot' });
    try {
      const pd = await PlayerData.findOne({ userId: socket.userId });
      if (!pd) return socket.emit('equip_result', { success: false, error: 'Player not found' });

      // Find item in inventory
      const invItems = pd.inventory?.items || [];
      const itemIdx  = invItems.findIndex(it => it && (it.id === itemId || it.itemId === itemId));
      if (itemIdx === -1) return socket.emit('equip_result', { success: false, error: 'Item not in inventory' });

      const item = invItems[itemIdx];
      const ship = pd.ship || {};
      if (!ship.modules) ship.modules = {};

      // Unequip whatever is currently in that slot back to inventory
      const currentlyEquipped = ship.modules[slot];
      if (currentlyEquipped) {
        invItems.push({ ...currentlyEquipped, source: 'unequipped', obtainedAt: Date.now() });
      }

      // Remove item from inventory and equip
      invItems.splice(itemIdx, 1);
      ship.modules[slot] = { ...item, equippedAt: Date.now() };
      pd.ship = ship;
      pd.markModified('ship');
      pd.markModified('inventory');
      await pd.save();

      socket.emit('equip_result', {
        success: true,
        slot,
        equipped: ship.modules[slot],
        unequipped: currentlyEquipped || null,
      });
      socket.emit('ship_modules_data', {
        shipId: ship.id || ship.shipId || null,
        modules: ship.modules,
        availableSlots: _getShipSlots(ship),
      });
      socket.emit('inventory_update', { items: invItems, maxSize: pd.inventory?.maxSize || 50 });

      console.log(`[MODULES] ${socket.username} equipped ${itemId} in slot ${slot}`);
    } catch (err) {
      console.error('[MODULES] equip_module error:', err);
      socket.emit('equip_result', { success: false, error: 'Server error' });
    }
  });

  // Unequip a module from a slot back to inventory
  socket.on('unequip_module', async ({ slot } = {}) => {
    if (!socket.userId || !slot) return;
    try {
      const pd = await PlayerData.findOne({ userId: socket.userId });
      if (!pd) return;
      const ship = pd.ship || {};
      if (!ship.modules?.[slot]) return socket.emit('equip_result', { success: false, error: 'Nothing equipped in that slot' });

      const item = ship.modules[slot];
      delete ship.modules[slot];
      const invItems = pd.inventory?.items || [];
      invItems.push({ ...item, source: 'unequipped', obtainedAt: Date.now() });
      pd.ship = ship;
      pd.markModified('ship');
      pd.markModified('inventory');
      await pd.save();

      socket.emit('equip_result', { success: true, slot, unequipped: item });
      socket.emit('ship_modules_data', { shipId: ship.id || null, modules: ship.modules, availableSlots: _getShipSlots(ship) });
      socket.emit('inventory_update', { items: invItems, maxSize: pd.inventory?.maxSize || 50 });
    } catch (err) {
      console.error('[MODULES] unequip_module error:', err);
    }
  });

  // ════════════════════════════════════════════════════════════════════════
  // PVP CHALLENGE SYSTEM (GDD §9.4)
  // ════════════════════════════════════════════════════════════════════════

  // Challenge another online player
  socket.on('pvp_challenge', async ({ targetUsername } = {}) => {
    if (!socket.userId || !targetUsername) return;
    if (targetUsername === socket.username) return socket.emit('pvp_result', { success: false, error: 'Cannot challenge yourself' });

    // Find target socket
    const targetEntry = [...connectedClients.entries()].find(([, c]) => c.username === targetUsername);
    if (!targetEntry) return socket.emit('pvp_result', { success: false, error: 'Player not online' });

    const [targetSocketId, targetClient] = targetEntry;
    const challengeId = `pvp_${Date.now()}_${Math.random().toString(36).substr(2, 6)}`;

    // Store pending challenge
    if (!pvpChallenges) pvpChallenges = new Map();
    pvpChallenges.set(challengeId, {
      challengerId: socket.userId,
      challengerName: socket.username,
      targetId: targetClient.userId,
      targetName: targetUsername,
      createdAt: Date.now(),
      status: 'pending',
    });

    // Notify challenger
    socket.emit('pvp_challenge_sent', { challengeId, targetUsername, message: `Challenge sent to ${targetUsername}!` });

    // Notify target
    io.to(targetSocketId).emit('pvp_challenge_received', {
      challengeId,
      challengerName: socket.username,
      message: `${socket.username} challenges you to PvP combat!`,
    });

    // Auto-expire after 30 seconds
    setTimeout(() => {
      if (pvpChallenges && pvpChallenges.get(challengeId)?.status === 'pending') {
        pvpChallenges.delete(challengeId);
        socket.emit('pvp_challenge_expired', { challengeId });
        io.to(targetSocketId).emit('pvp_challenge_expired', { challengeId });
      }
    }, 30000);
  });

  // Accept a PvP challenge
  socket.on('pvp_accept', async ({ challengeId } = {}) => {
    if (!socket.userId || !challengeId) return;
    if (!pvpChallenges?.has(challengeId)) return socket.emit('pvp_result', { success: false, error: 'Challenge not found or expired' });

    const challenge = pvpChallenges.get(challengeId);
    if (challenge.targetId !== socket.userId) return socket.emit('pvp_result', { success: false, error: 'Not your challenge' });
    if (challenge.status !== 'pending') return socket.emit('pvp_result', { success: false, error: 'Challenge already resolved' });

    challenge.status = 'active';

    try {
      // Load both players
      const [challenger, defender] = await Promise.all([
        PlayerData.findOne({ userId: challenge.challengerId }),
        PlayerData.findOne({ userId: challenge.targetId }),
      ]);
      if (!challenger || !defender) return socket.emit('pvp_result', { success: false, error: 'Player data not found' });

      // Simulate PvP battle
      const result = _simulatePvpBattle(challenger, defender);

      // Apply outcomes
      const winnerId = result.winner === 'challenger' ? challenge.challengerId : challenge.targetId;
      const loserId  = result.winner === 'challenger' ? challenge.targetId    : challenge.challengerId;

      const winnerPd = result.winner === 'challenger' ? challenger : defender;
      const loserPd  = result.winner === 'challenger' ? defender   : challenger;

      // Rewards
      const creditsWon = Math.floor(100 + (loserPd.level || 1) * 20);
      const xpWon      = Math.floor(50  + (loserPd.level || 1) * 10);
      winnerPd.credits = (winnerPd.credits || 0) + creditsWon;
      winnerPd.experience = (winnerPd.experience || 0) + xpWon;

      // Record in combat log
      const battleLog = {
        type: 'pvp',
        opponent: result.winner === 'challenger' ? challenge.targetName : challenge.challengerName,
        result: 'victory',
        creditsWon,
        xpWon,
        rounds: result.rounds,
        timestamp: Date.now(),
      };

      await Promise.all([winnerPd.save(), loserPd.save()]);
      pvpChallenges.delete(challengeId);

      // Find challenger socket
      const challengerEntry = [...connectedClients.entries()].find(([, c]) => c.userId === challenge.challengerId);

      const battleSummary = {
        success: true,
        challengeId,
        winner: result.winner === 'challenger' ? challenge.challengerName : challenge.targetName,
        loser:  result.winner === 'challenger' ? challenge.targetName    : challenge.challengerName,
        rounds: result.rounds,
        creditsWon,
        xpWon,
      };

      socket.emit('pvp_result', { ...battleSummary, youWon: challenge.targetId === winnerId });
      if (challengerEntry) {
        io.to(challengerEntry[0]).emit('pvp_result', { ...battleSummary, youWon: challenge.challengerId === winnerId });
      }

      console.log(`[PVP] ${challenge.challengerName} vs ${challenge.targetName} — winner: ${battleSummary.winner}`);
    } catch (err) {
      console.error('[PVP] pvp_accept error:', err);
      pvpChallenges.delete(challengeId);
      socket.emit('pvp_result', { success: false, error: 'Battle error' });
    }
  });

  // Decline a PvP challenge
  socket.on('pvp_decline', ({ challengeId } = {}) => {
    if (!challengeId || !pvpChallenges?.has(challengeId)) return;
    const challenge = pvpChallenges.get(challengeId);
    pvpChallenges.delete(challengeId);
    const challengerEntry = [...connectedClients.entries()].find(([, c]) => c.userId === challenge.challengerId);
    if (challengerEntry) {
      io.to(challengerEntry[0]).emit('pvp_declined', { challengeId, targetName: challenge.targetName });
    }
    socket.emit('pvp_result', { success: false, declined: true });
  });

  // ════════════════════════════════════════════════════════════════════════
  // SEASON LEADERBOARD (GDD §20.3)
  // ════════════════════════════════════════════════════════════════════════

  socket.on('get_season_leaderboard', async ({ limit = 50 } = {}) => {
    try {
      const season = seasonSystem.getCurrentSeason();
      if (!season.active) return socket.emit('season_leaderboard_data', { entries: [], season: null });

      // Get top players by credits+level score
      const players = await PlayerData.find({})
        .select('username level credits experience reputation fleetMissions')
        .limit(200)
        .lean();

      const scored = players
        .map(p => ({
          username: p.username || 'Unknown',
          level: p.level || 1,
          score: seasonSystem.getSeasonScore(p),
          credits: p.credits || 0,
        }))
        .sort((a, b) => b.score - a.score)
        .slice(0, limit)
        .map((p, i) => ({ rank: i + 1, ...p }));

      socket.emit('season_leaderboard_data', {
        entries: scored,
        season: season.name,
        endsAt: season.endsAt,
      });
    } catch (err) {
      console.error('[SEASON LEADERBOARD] error:', err);
      socket.emit('season_leaderboard_data', { entries: [], season: null });
    }
  });

  socket.on('disconnect', async () => {
    console.log('[GAME SERVER] Client disconnected:', socket.id);
    
    const clientData = connectedClients.get(socket.id);
    if (clientData) {
      // Save player data before disconnect
      if (clientData.playerData && clientData.userId) {
        try {
          // Final resource tick to capture any partial production
          resourceSystem.initResources(clientData.playerData);
          resourceSystem.tick(clientData.playerData);
          clientData.playerData.leaveServer();
          await savePlayerData(clientData.userId, clientData.playerData);
          console.log(`[GAME SERVER] Saved data for ${clientData.username} on disconnect`);
        } catch (error) {
          console.error('[GAME SERVER] Error saving data on disconnect:', error);
        }
      }
      
      // Handle game instance cleanup
      if (clientData.instanceId) {
        const instance = gameInstances.get(clientData.instanceId);
        if (instance) {
          instance.players.delete(socket.id);
          
          // Clean up empty instances
          if (instance.players.size === 0) {
            gameInstances.delete(clientData.instanceId);
          }
          
          // Notify other players
          socket.to(clientData.instanceId).emit('playerLeftInstance', {
            playerId: socket.id,
            playerCount: instance.players.size
          });
        }
      }
    }
    
    connectedClients.delete(socket.id);
    
    // Update player count on API server
    updatePlayerCountOnAPI();
  });
});

// Load player data from database
async function loadPlayerData(userId, username) {
  try {
    // Check if database is connected
    if (mongoose.connection.readyState !== 1) {
      console.error('[GAME SERVER] Database not connected, cannot load player data');
      return null;
    }
    
    let playerData = await PlayerData.findOne({ userId });
    
    if (!playerData) {
      // Create new player data with initialized systems
      console.log(`[GAME SERVER] Creating new player data for ${username} with system initialization`);
      
      // Initialize player data in all systems
      const questData = questSystem.initializePlayerData(userId);
      const skillData = skillSystem.initializePlayerData(userId);
      const craftingData = craftingSystem.initializePlayerData(userId);
      
      // For dungeons, initialize with available dungeons
      const availableDungeons = dungeonSystem.getAllDungeons();
      const dungeonData = {
        availableDungeons: availableDungeons.map(d => ({
          id: d.id,
          name: d.name,
          difficulty: d.difficulty,
          minLevel: d.minLevel || 1,
          maxPlayers: d.maxPlayers || 4,
          description: d.description
        })),
        completedDungeons: [],
        currentInstance: null,
        dungeonProgress: {}
      };
      
      playerData = new PlayerData({
        userId,
        username,
        resources: { metal: 500, gas: 200, crystal: 100, energyCells: 300, darkMatter: 0, lastTick: Date.now() },
        stats: {
          level: 1,
          experience: 0,
          totalExperience: 0,
          credits: 1000,
          gems: 50, // Give some starting gems
          skillPoints: 0,
          totalKills: 0,
          dungeonsCleared: 0,
          questsCompleted: 0,
          playTime: 0,
          lastLogin: new Date()
        },
        // Initialize with system data
        quests: {
          active: Array.from(questData.activeQuests.values()),
          completed: Array.from(questData.completedQuests.keys())
        },
        skills: skillData,
        dungeonSystem: dungeonData,
        crafting: craftingData
      });
      await playerData.save();
      console.log(`[GAME SERVER] Created new player data for ${username} with ${questData.activeQuests.size} active quests`);
    } else {
      // Auto-migration for existing players - check for missing data
      console.log(`[GAME SERVER] Loading existing player data for ${username} (Level ${playerData.stats.level})`);
      
      let migrated = false;
      let migrationLog = [];
      
      // Always categorize quests to ensure proper format
      console.log(`[GAME SERVER] Categorizing quests for ${username}`);
      console.log(`[GAME SERVER] Current playerData.quests before categorization:`, playerData.quests);
      
      const questData = questSystem.getPlayerData(userId);
      console.log(`[GAME SERVER] QuestSystem data:`, {
        activeQuestsCount: questData.activeQuests.size,
        completedQuestsCount: questData.completedQuests.size,
        activeQuests: Array.from(questData.activeQuests.values())
      });
      
      // Categorize quests by type for client
      const activeQuests = Array.from(questData.activeQuests.values());
      const mainQuests = activeQuests.filter(quest => quest.type === 'main');
      const dailyQuests = activeQuests.filter(quest => quest.type === 'daily');
      const weeklyQuests = activeQuests.filter(quest => quest.type === 'weekly');
      const tutorialQuests = activeQuests.filter(quest => quest.type === 'tutorial');
      
      playerData.quests = {
        main: mainQuests,
        daily: dailyQuests,
        weekly: weeklyQuests,
        tutorial: tutorialQuests,
        active: activeQuests,
        completed: Array.from(questData.completedQuests.keys())
      };
      
      console.log(`[GAME SERVER] Quest categorization complete:`, {
        main: mainQuests.length,
        daily: dailyQuests.length,
        weekly: weeklyQuests.length,
        tutorial: tutorialQuests.length,
        total: activeQuests.length
      });
      
      console.log(`[GAME SERVER] Final playerData.quests after categorization:`, playerData.quests);
      
      // IMPORTANT: Save the updated quest data to database
      try {
        await savePlayerData(userId, playerData);
        console.log(`[GAME SERVER] Saved categorized quest data for ${username}`);
      } catch (error) {
        console.error(`[GAME SERVER] Failed to save quest data for ${username}:`, error);
      }
      
      // Migrate skills if missing or empty
      if (!playerData.skills || Object.keys(playerData.skills).length === 0) {
        console.log(`[GAME SERVER] Migrating skills for ${username}`);
        const skillData = skillSystem.initializePlayerData(userId);
        playerData.skills = skillData;
        migrated = true;
        migrationLog.push('Added skill data');
      }
      
      // Migrate dungeons if missing or empty
      if (!playerData.dungeonSystem || !playerData.dungeonSystem.availableDungeons || 
          (playerData.dungeonSystem.availableDungeons && playerData.dungeonSystem.availableDungeons.length === 0)) {
        console.log(`[GAME SERVER] Migrating dungeons for ${username}`);
        const availableDungeons = dungeonSystem.getAllDungeons();
        playerData.dungeonSystem = {
          availableDungeons: availableDungeons.map(d => ({
            id: d.id,
            name: d.name,
            difficulty: d.difficulty,
            minLevel: d.minLevel || 1,
            maxPlayers: d.maxPlayers || 4,
            description: d.description
          })),
          completedDungeons: [],
          currentInstance: null,
          dungeonProgress: {}
        };
        migrated = true;
        migrationLog.push(`Added ${availableDungeons.length} available dungeons`);
      }
      
      // Migrate crafting if missing or empty (this field might not exist at all)
      if (!playerData.crafting || Object.keys(playerData.crafting).length === 0) {
        console.log(`[GAME SERVER] Migrating crafting for ${username}`);
        const craftingData = craftingSystem.initializePlayerData(userId);
        playerData.crafting = craftingData;
        migrated = true;
        migrationLog.push('Added crafting data');
      }
      
      // Initialize idle system data
      const idleData = idleSystem.initializePlayerData(userId);
      if (!playerData.idleSystem) {
        playerData.idleSystem = {
          lastActive: new Date().toISOString(),
          totalOfflineTime: 0,
          totalIdleCredits: 0
        };
        migrated = true;
        migrationLog.push('Added idle system data');
      }
      
      // Save if migration occurred
      if (migrated) {
        await playerData.save();
        console.log(`[GAME SERVER] Migration completed for ${username}: ${migrationLog.join(', ')}`);
      }
      
      // Update existing player login info
      playerData.username = username;
      playerData.lastLogin = new Date();
      await playerData.save();
    }
    
    return playerData;
  } catch (error) {
    console.error('[GAME SERVER] Error loading player data:', error);
    return null;
  }
}

// Save player data to database
async function savePlayerData(userId, playerData) {
  try {
    // Check if database is connected
    if (mongoose.connection.readyState !== 1) {
      console.error('[GAME SERVER] Database not connected, cannot save player data');
      return false;
    }
    
    await PlayerData.findOneAndUpdate(
      { userId },
      playerData,
      { upsert: true, new: true }
    );
    console.log(`[GAME SERVER] Saved player data for ${playerData.username}`);
    return true;
  } catch (error) {
    console.error('[GAME SERVER] Error saving player data:', error);
    return false;
  }
}

// Update player count on API server
async function updatePlayerCountOnAPI() {
  try {
    const apiServerUrl = 'http://localhost:3001';
    const currentPlayers = connectedClients.size;
    
    console.log(`[GAME SERVER] Updating player count: ${currentPlayers} players`);
    
    const response = await fetch(`${apiServerUrl}/api/servers/update-status/devgame-server-3002`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        currentPlayers: currentPlayers,
        status: currentPlayers > 0 ? 'active' : 'waiting'
      })
    });
    
    if (response.ok) {
      const result = await response.json();
      console.log(`[GAME SERVER] Player count updated:`, result);
    } else {
      console.error(`[GAME SERVER] Failed to update player count: ${response.status}`);
    }
  } catch (error) {
    console.error('[GAME SERVER] Error updating player count:', error);
  }
}

// Start server
const PORT = process.env.PORT || 3002;

async function startServer() {
  try {
    console.log('[GAME SERVER] Attempting to connect to database...');
    
    // Connect to database
    await connectDB();
    
    console.log('[GAME SERVER] Database connection established');
    
    server.listen(PORT, async () => {
      console.log(`[GAME SERVER] Game server running on port ${PORT}`);
      console.log(`[GAME SERVER] Socket.IO ready for multiplayer connections`);
      
      // Register with API server
      await registerWithAPIServer();
      
      // Start periodic player count updates
      setInterval(updatePlayerCountOnAPI, 30000); // Update every 30 seconds
      
      // Start online idle rewards generation (every 10 seconds)
      setInterval(async () => {
        console.log('[GAME SERVER] Idle reward timer triggered - checking', connectedClients.size, 'connected clients');
        
        for (const [socketId, clientData] of connectedClients.entries()) {
          if (clientData.userId && clientData.playerData) {
            try {
              console.log('[GAME SERVER] Processing idle rewards for client:', socketId, 'user:', clientData.username);
              // Update playTime for active players
              const sessionTime = clientData.playerData.updatePlayTime();
              console.log(`[GAME SERVER] Updated playTime for ${clientData.username}: +${sessionTime}ms, Total: ${clientData.playerData.stats.playTime}ms`);
              
              // Send playTime update to client
              io.to(socketId).emit('playTimeUpdated', {
                playTime: clientData.playerData.stats.playTime,
                sessionTime: sessionTime
              });
              
              const onlineRewards = idleSystem.generateOnlineIdleRewards(clientData.userId, 10000); // 10 seconds
              
              console.log('[GAME SERVER] Generated online rewards for', clientData.username, ':', onlineRewards);
              
              if (onlineRewards.credits > 0) {
                // Update player data with online rewards
                clientData.playerData.stats.credits = (clientData.playerData.stats.credits || 0) + onlineRewards.credits;
                clientData.playerData.stats.experience = (clientData.playerData.stats.experience || 0) + onlineRewards.experience;
                
                console.log('[GAME SERVER] Applied idle rewards - Credits:', onlineRewards.credits, 'New balance:', clientData.playerData.stats.credits);
                
                // Send update to client
                io.to(socketId).emit('onlineIdleRewards', {
                  credits: onlineRewards.credits,
                  experience: onlineRewards.experience,
                  newBalance: clientData.playerData.stats.credits,
                  playTime: clientData.playerData.stats.playTime
                });
                
                console.log('[GAME SERVER] Sent onlineIdleRewards to client:', socketId);
              } else {
                console.log('[GAME SERVER] No idle rewards generated for', clientData.username);
              }
            } catch (error) {
              console.error(`[GAME SERVER] Error generating online idle rewards for ${socketId}:`, error);
            }
          }
        }
      }, 10000); // Every 10 seconds

      // ── Market Tick: expire old listings every 15 min (GDD §18.2) ────────────
      setInterval(async () => {
        try { const n = await marketSystem.expireListings(); if(n>0) console.log(`[MARKET TICK] Expired ${n} listings`); }
        catch(e) { console.error('[MARKET TICK]', e.message); }
      }, 15 * 60 * 1000);

      // ── Auto-collect missions on login (handled in get_missions) ─────────────

      // ── Economy Tick: resource production every 60s (GDD §18.2) ────────────
      setInterval(async () => {
        for (const [socketId, clientData] of connectedClients.entries()) {
          if (!clientData.userId || !clientData.playerData) continue;
          try {
            resourceSystem.initResources(clientData.playerData);
            const { produced, capped } = resourceSystem.tick(clientData.playerData);
            if (Object.keys(produced).length > 0) {
              io.to(socketId).emit('resource_update', {
                resources: clientData.playerData.resources,
                produced,
                capped,
                rates: resourceSystem.getProductionRates(clientData.playerData),
                caps:  resourceSystem.getStorageCaps(clientData.playerData),
              });
            }
          } catch (err) {
            console.error('[ECONOMY TICK] Error:', err.message);
          }
        }
      }, 60000); // GDD §18.2: 60-second economy tick

    });
  } catch (error) {
    console.error('[GAME SERVER] Failed to start server:', error);
    process.exit(1);
  }
}

// Register this GameServer with the API server
async function registerWithAPIServer() {
  try {
    // Force use of local API server for development
    const apiServerUrl = 'http://localhost:3001';
    const gameServerUrl = 'https://dev.gameserver.galaxystrike.online';
    
    const serverData = {
      serverId: `devgame-server-${PORT}`,
      name: 'Dev Game Server',
      type: 'public', // Must be 'public' or 'private' according to model
      region: 'Europe',
      maxPlayers: 100, // Hardcoded to allow 100 players
      currentPlayers: 0,
      gameServerUrl: gameServerUrl,
      owner: {
        userId: 'developer',
        username: 'Developer'
      }
    };
    
    console.log(`[GAME SERVER] Registering with API server at ${apiServerUrl}`);
    console.log(`[GAME SERVER] Server data:`, serverData);
    
    const response = await fetch(`${apiServerUrl}/api/servers/register`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(serverData)
    });
    
    if (response.ok) {
      const result = await response.json();
      console.log(`[GAME SERVER] Successfully registered with API server:`, result);
    } else {
      const errorText = await response.text();
      console.error(`[GAME SERVER] Failed to register with API server: ${response.status}`);
      console.error(`[GAME SERVER] Error response:`, errorText);
    }
  } catch (error) {
    console.error('[GAME SERVER] Error registering with API server:', error);
  }
}

startServer();

module.exports = { app, server, io, gameInstances, connectedClients };
