const express = require('express');
const crypto = require('crypto');
const { authenticateToken } = require('../middleware/auth');
const db = require('../database');
const { triggerWebhook } = require('../utils/webhooks');
const { getWebSocketManager } = require('../utils/websocket-manager');
const { startMonitor, stopMonitor, getMonitorSnapshot } = require('../utils/ecast-shard-client');
const { computeNotesPreview } = require('../utils/notes-preview');
const { optionalAuthenticateToken } = require('../middleware/optional-auth');

const router = express.Router();

// Helper function to create a hash of a message
function createMessageHash(username, message, timestamp) {
  return crypto
    .createHash('sha256')
    .update(`${username}:${message}:${timestamp}`)
    .digest('hex');
}

// Get all sessions
router.get('/', (req, res) => {
  try {
    const filter = req.query.filter || 'default';
    const limitParam = req.query.limit || 'all';
    const offsetParam = req.query.offset || '0';
    let offset = parseInt(offsetParam, 10);
    if (isNaN(offset) || offset < 0) offset = 0;

    let whereClause = '';
    if (filter === 'default') {
      whereClause = 'WHERE s.archived = 0';
    } else if (filter === 'archived') {
      whereClause = 'WHERE s.archived = 1';
    }

    const countRow = db.prepare(`
      SELECT COUNT(DISTINCT s.id) as total
      FROM sessions s
      ${whereClause}
    `).get();

    let limitClause = '';
    if (limitParam !== 'all') {
      const limitNum = parseInt(limitParam, 10);
      if (!isNaN(limitNum) && limitNum > 0) {
        limitClause = `LIMIT ${limitNum}`;
      }
    }

    let offsetClause = '';
    if (offset > 0) {
      offsetClause = `OFFSET ${offset}`;
    }

    const sessions = db.prepare(`
      SELECT 
        s.id,
        s.created_at,
        s.closed_at,
        s.is_active,
        s.archived,
        s.notes,
        COUNT(sg.id) as games_played
      FROM sessions s
      LEFT JOIN session_games sg ON s.id = sg.session_id
      ${whereClause}
      GROUP BY s.id
      ORDER BY s.created_at DESC
      ${limitClause}
      ${offsetClause}
    `).all();

    const result = sessions.map(({ notes, ...session }) => {
      const { has_notes, notes_preview } = computeNotesPreview(notes);
      return { ...session, has_notes, notes_preview };
    });

    const absoluteTotal = db.prepare('SELECT COUNT(*) as total FROM sessions').get();

    if (offset > 0) {
      const prevRow = db.prepare(`
        SELECT s.created_at
        FROM sessions s
        ${whereClause}
        ORDER BY s.created_at DESC
        LIMIT 1 OFFSET ${offset - 1}
      `).get();
      if (prevRow) {
        res.set('X-Prev-Last-Date', prevRow.created_at);
      }
    }

    res.set('X-Total-Count', String(countRow.total));
    res.set('X-Absolute-Total', String(absoluteTotal.total));
    res.json(result);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// Get active session
router.get('/active', (req, res) => {
  try {
    const session = db.prepare(`
      SELECT 
        s.*,
        COUNT(sg.id) as games_played
      FROM sessions s
      LEFT JOIN session_games sg ON s.id = sg.session_id
      WHERE s.is_active = 1
      GROUP BY s.id
      LIMIT 1
    `).get();

    // Return null instead of 404 when no active session
    if (!session) {
      return res.json({ session: null, message: 'No active session' });
    }

    res.json(session);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// Get single session by ID
router.get('/:id', optionalAuthenticateToken, (req, res) => {
  try {
    const session = db.prepare(`
      SELECT 
        s.*,
        COUNT(sg.id) as games_played
      FROM sessions s
      LEFT JOIN session_games sg ON s.id = sg.session_id
      WHERE s.id = ?
      GROUP BY s.id
    `).get(req.params.id);

    if (!session) {
      return res.status(404).json({ error: 'Session not found' });
    }

    const { has_notes, notes_preview } = computeNotesPreview(session.notes);

    if (req.user) {
      res.json({ ...session, has_notes, notes_preview });
    } else {
      const { notes, ...publicSession } = session;
      res.json({ ...publicSession, has_notes, notes_preview });
    }
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// Create new session (admin only)
router.post('/', authenticateToken, (req, res) => {
  try {
    const { notes } = req.body;

    // Check if there's already an active session
    const activeSession = db.prepare('SELECT id FROM sessions WHERE is_active = 1').get();
    
    if (activeSession) {
      return res.status(400).json({ 
        error: 'An active session already exists. Please close it before creating a new one.',
        activeSessionId: activeSession.id
      });
    }

    const stmt = db.prepare(`
      INSERT INTO sessions (notes, is_active) 
      VALUES (?, 1)
    `);

    const result = stmt.run(notes || null);
    const newSession = db.prepare('SELECT * FROM sessions WHERE id = ?').get(result.lastInsertRowid);

    // Broadcast session.started event via WebSocket to all authenticated clients
    try {
      const wsManager = getWebSocketManager();
      if (wsManager) {
        const eventData = {
          session: {
            id: newSession.id,
            is_active: 1,
            created_at: newSession.created_at,
            notes: newSession.notes
          }
        };
        
        wsManager.broadcastToAll('session.started', eventData);
        console.log(`[Sessions] Broadcasted session.started event for session ${newSession.id} to all clients`);
      }
    } catch (error) {
      // Log error but don't fail the request
      console.error('Error broadcasting session.started event:', error);
    }

    res.status(201).json(newSession);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// Bulk session operations (admin only)
router.post('/bulk', authenticateToken, (req, res) => {
  try {
    const { action, ids } = req.body;

    if (!Array.isArray(ids) || ids.length === 0) {
      return res.status(400).json({ error: 'ids must be a non-empty array' });
    }

    const validActions = ['archive', 'unarchive', 'delete'];
    if (!validActions.includes(action)) {
      return res.status(400).json({ error: `action must be one of: ${validActions.join(', ')}` });
    }

    const placeholders = ids.map(() => '?').join(',');
    const sessions = db.prepare(
      `SELECT id, is_active FROM sessions WHERE id IN (${placeholders})`
    ).all(...ids);

    if (sessions.length !== ids.length) {
      const foundIds = sessions.map(s => s.id);
      const missingIds = ids.filter(id => !foundIds.includes(id));
      return res.status(404).json({ error: 'Some sessions not found', missingIds });
    }

    if (action === 'archive' || action === 'delete') {
      const activeIds = sessions.filter(s => s.is_active === 1).map(s => s.id);
      if (activeIds.length > 0) {
        return res.status(400).json({
          error: `Cannot ${action} active sessions. Close them first.`,
          activeIds
        });
      }
    }

    const bulkOperation = db.transaction(() => {
      if (action === 'archive') {
        db.prepare(`UPDATE sessions SET archived = 1 WHERE id IN (${placeholders})`).run(...ids);
      } else if (action === 'unarchive') {
        db.prepare(`UPDATE sessions SET archived = 0 WHERE id IN (${placeholders})`).run(...ids);
      } else if (action === 'delete') {
        db.prepare(`DELETE FROM chat_logs WHERE session_id IN (${placeholders})`).run(...ids);
        db.prepare(`DELETE FROM live_votes WHERE session_id IN (${placeholders})`).run(...ids);
        db.prepare(`DELETE FROM session_games WHERE session_id IN (${placeholders})`).run(...ids);
        db.prepare(`DELETE FROM sessions WHERE id IN (${placeholders})`).run(...ids);
      }
    });

    bulkOperation();

    res.json({ success: true, affected: ids.length });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// Close/finalize session (admin only)
router.post('/:id/close', authenticateToken, (req, res) => {
  try {
    const { notes } = req.body;

    const session = db.prepare('SELECT * FROM sessions WHERE id = ?').get(req.params.id);
    
    if (!session) {
      return res.status(404).json({ error: 'Session not found' });
    }

    if (session.is_active === 0) {
      return res.status(400).json({ error: 'Session is already closed' });
    }

    // Set all 'playing' games to 'played' before closing
    db.prepare(`
      UPDATE session_games 
      SET status = 'played'
      WHERE session_id = ? AND status = 'playing'
    `).run(req.params.id);

    const stmt = db.prepare(`
      UPDATE sessions 
      SET is_active = 0, closed_at = CURRENT_TIMESTAMP, notes = COALESCE(?, notes)
      WHERE id = ?
    `);

    stmt.run(notes || null, req.params.id);

    // Get updated session with games count
    const closedSession = db.prepare(`
      SELECT 
        s.*,
        COUNT(sg.id) as games_played
      FROM sessions s
      LEFT JOIN session_games sg ON s.id = sg.session_id
      WHERE s.id = ?
      GROUP BY s.id
    `).get(req.params.id);

    // Broadcast session.ended event via WebSocket
    try {
      const wsManager = getWebSocketManager();
      if (wsManager) {
        const eventData = {
          session: {
            id: closedSession.id,
            is_active: 0,
            games_played: closedSession.games_played
          }
        };
        
        wsManager.broadcastEvent('session.ended', eventData, parseInt(req.params.id));
        console.log(`[Sessions] Broadcasted session.ended event for session ${req.params.id}`);
      }
    } catch (error) {
      // Log error but don't fail the request
      console.error('Error broadcasting session.ended event:', error);
    }

    res.json(closedSession);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// Start voting/polling for a session (broadcasts poll.start to subscribers)
router.post('/:id/voting/start', authenticateToken, (req, res) => {
  try {
    const sessionId = parseInt(req.params.id);
    const session = db.prepare('SELECT * FROM sessions WHERE id = ?').get(sessionId);

    if (!session) {
      return res.status(404).json({ error: 'Session not found' });
    }

    if (session.is_active === 0) {
      return res.status(400).json({ error: 'Session is not active' });
    }

    db.prepare(
      'UPDATE sessions SET poll_active = 1, poll_started_at = ?, poll_leading_game_id = NULL, poll_leading_label = NULL, poll_leading_votes = NULL WHERE id = ?'
    ).run(new Date().toISOString(), sessionId);

    const wsManager = getWebSocketManager();
    if (wsManager) {
      wsManager.broadcastEvent('poll.start', { sessionId }, sessionId);
    }

    res.json({ success: true });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// End voting/polling for a session (broadcasts voting.ended to subscribers)
router.post('/:id/voting/end', authenticateToken, (req, res) => {
  try {
    const sessionId = parseInt(req.params.id);
    const session = db.prepare('SELECT * FROM sessions WHERE id = ?').get(sessionId);

    if (!session) {
      return res.status(404).json({ error: 'Session not found' });
    }

    if (session.is_active === 0) {
      return res.status(400).json({ error: 'Session is not active' });
    }

    db.prepare(
      'UPDATE sessions SET poll_active = 0, poll_started_at = NULL WHERE id = ?'
    ).run(sessionId);

    const wsManager = getWebSocketManager();
    if (wsManager) {
      wsManager.broadcastEvent('voting.ended', { sessionId }, sessionId);
    }

    res.json({ success: true });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// Delete session (admin only)
router.delete('/:id', authenticateToken, (req, res) => {
  try {
    const session = db.prepare('SELECT * FROM sessions WHERE id = ?').get(req.params.id);
    
    if (!session) {
      return res.status(404).json({ error: 'Session not found' });
    }

    // Prevent deletion of active sessions
    if (session.is_active === 1) {
      return res.status(400).json({ error: 'Cannot delete an active session. Please close it first.' });
    }

    // Delete related data first (cascade)
    db.prepare('DELETE FROM chat_logs WHERE session_id = ?').run(req.params.id);
    db.prepare('DELETE FROM session_games WHERE session_id = ?').run(req.params.id);
    
    // Delete the session
    db.prepare('DELETE FROM sessions WHERE id = ?').run(req.params.id);

    res.json({ message: 'Session deleted successfully', sessionId: parseInt(req.params.id) });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// Update session notes (admin only)
router.put('/:id/notes', authenticateToken, (req, res) => {
  try {
    const { notes } = req.body;

    const session = db.prepare('SELECT id FROM sessions WHERE id = ?').get(req.params.id);
    if (!session) {
      return res.status(404).json({ error: 'Session not found' });
    }

    db.prepare('UPDATE sessions SET notes = ? WHERE id = ?').run(notes, req.params.id);

    const updated = db.prepare(`
      SELECT s.*, COUNT(sg.id) as games_played
      FROM sessions s
      LEFT JOIN session_games sg ON s.id = sg.session_id
      WHERE s.id = ?
      GROUP BY s.id
    `).get(req.params.id);

    res.json(updated);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// Clear session notes (admin only)
router.delete('/:id/notes', authenticateToken, (req, res) => {
  try {
    const session = db.prepare('SELECT id FROM sessions WHERE id = ?').get(req.params.id);
    if (!session) {
      return res.status(404).json({ error: 'Session not found' });
    }

    db.prepare('UPDATE sessions SET notes = NULL WHERE id = ?').run(req.params.id);

    res.json({ success: true });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// Archive a session (admin only)
router.post('/:id/archive', authenticateToken, (req, res) => {
  try {
    const session = db.prepare('SELECT id, is_active FROM sessions WHERE id = ?').get(req.params.id);

    if (!session) {
      return res.status(404).json({ error: 'Session not found' });
    }

    if (session.is_active === 1) {
      return res.status(400).json({ error: 'Cannot archive an active session. Please close it first.' });
    }

    db.prepare('UPDATE sessions SET archived = 1 WHERE id = ?').run(req.params.id);
    res.json({ success: true });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// Unarchive a session (admin only)
router.post('/:id/unarchive', authenticateToken, (req, res) => {
  try {
    const session = db.prepare('SELECT id FROM sessions WHERE id = ?').get(req.params.id);

    if (!session) {
      return res.status(404).json({ error: 'Session not found' });
    }

    db.prepare('UPDATE sessions SET archived = 0 WHERE id = ?').run(req.params.id);
    res.json({ success: true });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// Get games played in a session
router.get('/:id/games', (req, res) => {
  try {
    const games = db.prepare(`
      SELECT 
        sg.*,
        g.pack_name,
        g.title,
        g.game_type,
        g.min_players,
        g.max_players,
        g.popularity_score,
        g.upvotes,
        g.downvotes
      FROM session_games sg
      JOIN games g ON sg.game_id = g.id
      WHERE sg.session_id = ?
      ORDER BY sg.played_at ASC
    `).all(req.params.id);

    res.json(games);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// Get vote breakdown for a session
router.get('/:id/votes', (req, res) => {
  try {
    const session = db.prepare('SELECT id FROM sessions WHERE id = ?').get(req.params.id);

    if (!session) {
      return res.status(404).json({ error: 'Session not found' });
    }

    const votes = db.prepare(`
      SELECT
        lv.game_id,
        g.title,
        g.pack_name,
        SUM(CASE WHEN lv.vote_type = 1 THEN 1 ELSE 0 END) AS upvotes,
        SUM(CASE WHEN lv.vote_type = -1 THEN 1 ELSE 0 END) AS downvotes,
        SUM(lv.vote_type) AS net_score,
        COUNT(*) AS total_votes
      FROM live_votes lv
      JOIN games g ON lv.game_id = g.id
      WHERE lv.session_id = ?
      GROUP BY lv.game_id
      ORDER BY net_score DESC
    `).all(req.params.id);

    res.json({
      session_id: parseInt(req.params.id),
      votes,
    });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// Add game to session (admin only)
router.post('/:id/games', authenticateToken, (req, res) => {
  try {
    const { game_id, manually_added, room_code } = req.body;

    if (!game_id) {
      return res.status(400).json({ error: 'game_id is required' });
    }

    // Verify session exists and is active
    const session = db.prepare('SELECT * FROM sessions WHERE id = ?').get(req.params.id);
    
    if (!session) {
      return res.status(404).json({ error: 'Session not found' });
    }

    if (session.is_active === 0) {
      return res.status(400).json({ error: 'Cannot add games to a closed session' });
    }

    // Verify game exists
    const game = db.prepare('SELECT * FROM games WHERE id = ?').get(game_id);
    
    if (!game) {
      return res.status(404).json({ error: 'Game not found' });
    }

    // Stop monitors for currently-playing games before demoting them
    const previouslyPlaying = db.prepare(
      'SELECT id FROM session_games WHERE session_id = ? AND status = ?'
    ).all(req.params.id, 'playing');
    for (const prev of previouslyPlaying) {
      try { stopMonitor(req.params.id, prev.id); } catch (_) {}
    }

    // Set all current 'playing' games to 'played' (except skipped ones)
    db.prepare(`
      UPDATE session_games 
      SET status = CASE 
        WHEN status = 'skipped' THEN 'skipped'
        ELSE 'played'
      END
      WHERE session_id = ? AND status = 'playing'
    `).run(req.params.id);

    // Add game to session with 'playing' status
    const stmt = db.prepare(`
      INSERT INTO session_games (session_id, game_id, manually_added, status, room_code)
      VALUES (?, ?, ?, 'playing', ?)
    `);

    const result = stmt.run(req.params.id, game_id, manually_added ? 1 : 0, room_code || null);

    // Increment play count for the game
    db.prepare('UPDATE games SET play_count = play_count + 1 WHERE id = ?').run(game_id);

    const sessionGame = db.prepare(`
      SELECT 
        sg.*,
        g.pack_name,
        g.title,
        g.game_type,
        g.min_players,
        g.max_players
      FROM session_games sg
      JOIN games g ON sg.game_id = g.id
      WHERE sg.id = ?
    `).get(result.lastInsertRowid);

    // Trigger webhook and WebSocket for game.added event
    try {
      const sessionStats = db.prepare(`
        SELECT 
          s.*,
          COUNT(sg.id) as games_played
        FROM sessions s
        LEFT JOIN session_games sg ON s.id = sg.session_id
        WHERE s.id = ?
        GROUP BY s.id
      `).get(req.params.id);

      const eventData = {
        session: {
          id: sessionStats.id,
          is_active: sessionStats.is_active === 1,
          games_played: sessionStats.games_played
        },
        game: {
          id: game.id,
          title: game.title,
          pack_name: game.pack_name,
          min_players: game.min_players,
          max_players: game.max_players,
          manually_added: manually_added || false,
          room_code: room_code || null
        }
      };

      // Trigger webhook (for backwards compatibility)
      triggerWebhook('game.added', eventData);

      // Broadcast via WebSocket (new preferred method)
      const wsManager = getWebSocketManager();
      if (wsManager) {
        wsManager.broadcastEvent('game.added', eventData, parseInt(req.params.id));
      }
    } catch (error) {
      // Log error but don't fail the request
      console.error('Error triggering notifications:', error);
    }

    // Automatically start room monitoring if room code was provided
    if (room_code) {
      try {
        startMonitor(req.params.id, result.lastInsertRowid, room_code, game.max_players);
      } catch (error) {
        console.error('Error starting room monitor:', error);
      }
    }

    res.status(201).json(sessionGame);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// Import chat log and process popularity (admin only)
router.post('/:id/chat-import', authenticateToken, (req, res) => {
  try {
    const { chatData } = req.body;

    if (!chatData || !Array.isArray(chatData)) {
      return res.status(400).json({ error: 'chatData must be an array' });
    }

    const session = db.prepare('SELECT * FROM sessions WHERE id = ?').get(req.params.id);
    
    if (!session) {
      return res.status(404).json({ error: 'Session not found' });
    }

    // Get all games played in this session with timestamps
    const sessionGames = db.prepare(`
      SELECT sg.game_id, sg.played_at, g.title, g.upvotes, g.downvotes
      FROM session_games sg
      JOIN games g ON sg.game_id = g.id
      WHERE sg.session_id = ?
      ORDER BY sg.played_at ASC
    `).all(req.params.id);

    if (sessionGames.length === 0) {
      return res.status(400).json({ error: 'No games played in this session to match votes against' });
    }

    let votesProcessed = 0;
    let duplicatesSkipped = 0;
    const votesByGame = {};
    const sessionId = parseInt(req.params.id);
    const voteMatches = []; // Debug: track which votes matched to which games

    const insertChatLog = db.prepare(`
      INSERT INTO chat_logs (session_id, chatter_name, message, timestamp, parsed_vote, message_hash)
      VALUES (?, ?, ?, ?, ?, ?)
    `);

    const checkDuplicate = db.prepare(`
      SELECT id FROM chat_logs WHERE message_hash = ? LIMIT 1
    `);

    const updateUpvote = db.prepare(`
      UPDATE games SET upvotes = upvotes + 1, popularity_score = popularity_score + 1 WHERE id = ?
    `);

    const updateDownvote = db.prepare(`
      UPDATE games SET downvotes = downvotes + 1, popularity_score = popularity_score - 1 WHERE id = ?
    `);

    const processVotes = db.transaction((messages) => {
      for (const msg of messages) {
        const { username, message, timestamp } = msg;

        if (!username || !message || !timestamp) {
          continue;
        }

        // Create hash for this message
        const messageHash = createMessageHash(username, message, timestamp);

        // Check if we've already imported this exact message
        const existing = checkDuplicate.get(messageHash);
        if (existing) {
          duplicatesSkipped++;
          continue; // Skip this duplicate message
        }

        // Check for vote patterns
        let vote = null;
        if (message.includes('thisgame++')) {
          vote = 'thisgame++';
        } else if (message.includes('thisgame--')) {
          vote = 'thisgame--';
        }

        // Insert into chat logs with hash
        insertChatLog.run(
          sessionId,
          username,
          message,
          timestamp,
          vote,
          messageHash
        );

        if (vote) {
          // Find which game was being played at this timestamp
          const messageTime = new Date(timestamp).getTime();
          let matchedGame = null;

          for (let i = 0; i < sessionGames.length; i++) {
            const currentGame = sessionGames[i];
            const nextGame = sessionGames[i + 1];
            
            const currentGameTime = new Date(currentGame.played_at).getTime();
            
            if (nextGame) {
              const nextGameTime = new Date(nextGame.played_at).getTime();
              if (messageTime >= currentGameTime && messageTime < nextGameTime) {
                matchedGame = currentGame;
                break;
              }
            } else {
              // Last game in session
              if (messageTime >= currentGameTime) {
                matchedGame = currentGame;
                break;
              }
            }
          }

          if (matchedGame) {
            const isUpvote = vote === 'thisgame++';
            
            // Debug: track this vote match
            voteMatches.push({
              username: username,
              vote: vote,
              timestamp: timestamp,
              timestamp_ms: messageTime,
              matched_game: matchedGame.title,
              game_played_at: matchedGame.played_at,
              game_played_at_ms: new Date(matchedGame.played_at).getTime()
            });
            
            if (isUpvote) {
              updateUpvote.run(matchedGame.game_id);
            } else {
              updateDownvote.run(matchedGame.game_id);
            }
            
            if (!votesByGame[matchedGame.game_id]) {
              votesByGame[matchedGame.game_id] = {
                title: matchedGame.title,
                upvotes: 0,
                downvotes: 0
              };
            }
            
            if (isUpvote) {
              votesByGame[matchedGame.game_id].upvotes++;
            } else {
              votesByGame[matchedGame.game_id].downvotes++;
            }
            
            votesProcessed++;
          }
        }
      }
    });

    processVotes(chatData);

    res.json({
      message: 'Chat log imported and processed successfully',
      messagesImported: chatData.length,
      duplicatesSkipped,
      votesProcessed,
      votesByGame,
      debug: {
        sessionGamesTimeline: sessionGames.map(sg => ({
          title: sg.title,
          played_at: sg.played_at,
          played_at_ms: new Date(sg.played_at).getTime()
        })),
        voteMatches: voteMatches
      }
    });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// Update session game status (admin only)
router.patch('/:sessionId/games/:gameId/status', authenticateToken, (req, res) => {
  try {
    const { status } = req.body;
    const { sessionId, gameId } = req.params;

    if (!status || !['playing', 'played', 'skipped'].includes(status)) {
      return res.status(400).json({ error: 'Invalid status. Must be playing, played, or skipped' });
    }

    // If setting to 'playing', first stop monitors and demote other playing games
    if (status === 'playing') {
      const previouslyPlaying = db.prepare(
        'SELECT id FROM session_games WHERE session_id = ? AND status = ?'
      ).all(sessionId, 'playing');
      for (const prev of previouslyPlaying) {
        if (String(prev.id) !== String(gameId)) {
          try { stopMonitor(sessionId, prev.id); } catch (_) {}
        }
      }

      db.prepare(`
        UPDATE session_games 
        SET status = CASE 
          WHEN status = 'skipped' THEN 'skipped'
          ELSE 'played'
        END
        WHERE session_id = ? AND status = 'playing'
      `).run(sessionId);
    }

    // Update the specific game
    const result = db.prepare(`
      UPDATE session_games 
      SET status = ? 
      WHERE session_id = ? AND id = ?
    `).run(status, sessionId, gameId);

    if (result.changes === 0) {
      return res.status(404).json({ error: 'Session game not found' });
    }

    // Stop room monitor and player count check if game is no longer playing
    if (status !== 'playing') {
      try {
        stopMonitor(sessionId, gameId);
      } catch (error) {
        console.error('Error stopping room monitor/player count check:', error);
      }
    }

    res.json({ message: 'Status updated successfully', status });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// Delete session game (admin only)
router.delete('/:sessionId/games/:gameId', authenticateToken, (req, res) => {
  try {
    const { sessionId, gameId } = req.params;

    // Stop room monitor and player count check before deleting
    try {
      stopMonitor(sessionId, gameId);
    } catch (error) {
      console.error('Error stopping room monitor/player count check:', error);
    }

    const result = db.prepare(`
      DELETE FROM session_games 
      WHERE session_id = ? AND id = ?
    `).run(sessionId, gameId);

    if (result.changes === 0) {
      return res.status(404).json({ error: 'Session game not found' });
    }

    res.json({ message: 'Game removed from session successfully' });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// Update room code for a session game (admin only)
router.patch('/:sessionId/games/:gameId/room-code', authenticateToken, (req, res) => {
  try {
    const { sessionId, gameId } = req.params;
    const { room_code } = req.body;

    if (!room_code) {
      return res.status(400).json({ error: 'room_code is required' });
    }

    // Validate room code format: 4 characters, A-Z and 0-9 only
    const roomCodeRegex = /^[A-Z0-9]{4}$/;
    if (!roomCodeRegex.test(room_code)) {
      return res.status(400).json({ error: 'room_code must be exactly 4 alphanumeric characters (A-Z, 0-9)' });
    }

    // Update the room code
    const result = db.prepare(`
      UPDATE session_games 
      SET room_code = ? 
      WHERE session_id = ? AND id = ?
    `).run(room_code, sessionId, gameId);

    if (result.changes === 0) {
      return res.status(404).json({ error: 'Session game not found' });
    }

    // Return updated game data
    const updatedGame = db.prepare(`
      SELECT 
        sg.*,
        g.pack_name,
        g.title,
        g.game_type,
        g.min_players,
        g.max_players,
        g.popularity_score,
        g.upvotes,
        g.downvotes
      FROM session_games sg
      JOIN games g ON sg.game_id = g.id
      WHERE sg.session_id = ? AND sg.id = ?
    `).get(sessionId, gameId);

    res.json(updatedGame);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// Export session data (plaintext and JSON)
router.get('/:id/export', authenticateToken, (req, res) => {
  try {
    const { format } = req.query; // 'json' or 'txt'
    const sessionId = req.params.id;

    // Get session info
    const session = db.prepare(`
      SELECT 
        s.*,
        COUNT(sg.id) as games_played
      FROM sessions s
      LEFT JOIN session_games sg ON s.id = sg.session_id
      WHERE s.id = ?
      GROUP BY s.id
    `).get(sessionId);

    if (!session) {
      return res.status(404).json({ error: 'Session not found' });
    }

    // Get games for this session
    const games = db.prepare(`
      SELECT 
        sg.*,
        g.title,
        g.pack_name,
        g.min_players,
        g.max_players,
        g.game_type
      FROM session_games sg
      JOIN games g ON sg.game_id = g.id
      WHERE sg.session_id = ?
      ORDER BY sg.played_at ASC
    `).all(sessionId);

    // Get chat logs if any
    const chatLogs = db.prepare(`
      SELECT * FROM chat_logs
      WHERE session_id = ?
      ORDER BY timestamp ASC
    `).all(sessionId);

    if (format === 'json') {
      // JSON Export
      const exportData = {
        session: {
          id: session.id,
          created_at: session.created_at,
          closed_at: session.closed_at,
          is_active: session.is_active === 1,
          notes: session.notes,
          games_played: session.games_played
        },
        games: games.map(game => ({
          title: game.title,
          pack: game.pack_name,
          players: `${game.min_players}-${game.max_players}`,
          type: game.game_type,
          played_at: game.played_at,
          manually_added: game.manually_added === 1,
          status: game.status
        })),
        chat_logs: chatLogs.map(log => ({
          username: log.chatter_name,
          message: log.message,
          timestamp: log.timestamp,
          vote: log.parsed_vote
        }))
      };

      res.setHeader('Content-Type', 'application/json');
      res.setHeader('Content-Disposition', `attachment; filename="session-${sessionId}.json"`);
      res.send(JSON.stringify(exportData, null, 2));
    } else {
      // Plain Text Export
      let text = `JACKBOX GAME PICKER - SESSION EXPORT\n`;
      text += `${'='.repeat(50)}\n\n`;
      text += `Session ID: ${session.id}\n`;
      text += `Created: ${session.created_at}\n`;
      if (session.closed_at) {
        text += `Closed: ${session.closed_at}\n`;
      }
      text += `Status: ${session.is_active ? 'Active' : 'Ended'}\n`;
      if (session.notes) {
        text += `Notes: ${session.notes}\n`;
      }
      text += `\nGames Played: ${session.games_played}\n`;
      text += `\n${'='.repeat(50)}\n\n`;

      if (games.length > 0) {
        text += `GAMES:\n`;
        text += `${'-'.repeat(50)}\n`;
        games.forEach((game, index) => {
          text += `\n${index + 1}. ${game.title}\n`;
          text += `   Pack: ${game.pack_name}\n`;
          text += `   Players: ${game.min_players}-${game.max_players}\n`;
          if (game.game_type) {
            text += `   Type: ${game.game_type}\n`;
          }
          text += `   Played: ${game.played_at}\n`;
          text += `   Status: ${game.status}\n`;
          if (game.manually_added === 1) {
            text += `   (Manually Added)\n`;
          }
        });
        text += `\n${'-'.repeat(50)}\n`;
      }

      if (chatLogs.length > 0) {
        text += `\nCHAT LOGS:\n`;
        text += `${'-'.repeat(50)}\n`;
        chatLogs.forEach(log => {
          text += `\n[${log.timestamp}] ${log.chatter_name}:\n`;
          text += `  ${log.message}\n`;
          if (log.parsed_vote) {
            text += `  Vote: ${log.parsed_vote}\n`;
          }
        });
        text += `\n${'-'.repeat(50)}\n`;
      }

      text += `\nEnd of Session Export\n`;

      res.setHeader('Content-Type', 'text/plain');
      res.setHeader('Content-Disposition', `attachment; filename="session-${sessionId}.txt"`);
      res.send(text);
    }
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// Get live game status from shard monitor or DB fallback
router.get('/:sessionId/games/:gameId/status-live', (req, res) => {
  try {
    const { sessionId, gameId } = req.params;

    const snapshot = getMonitorSnapshot(sessionId, gameId);
    if (snapshot) {
      return res.json(snapshot);
    }

    const game = db.prepare(`
      SELECT 
        sg.room_code,
        sg.player_count,
        sg.player_count_check_status,
        g.title,
        g.pack_name,
        g.max_players
      FROM session_games sg
      JOIN games g ON sg.game_id = g.id
      WHERE sg.session_id = ? AND sg.id = ?
    `).get(sessionId, gameId);

    if (!game) {
      return res.status(404).json({ error: 'Session game not found' });
    }

    res.json({
      sessionId: parseInt(sessionId, 10),
      gameId: parseInt(gameId, 10),
      roomCode: game.room_code,
      appTag: null,
      maxPlayers: game.max_players,
      playerCount: game.player_count,
      players: [],
      lobbyState: null,
      gameState: null,
      gameStarted: false,
      gameFinished: game.player_count_check_status === 'completed',
      monitoring: false,
      title: game.title,
      packName: game.pack_name,
      status: game.player_count_check_status,
    });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// Start player count check for a session game (admin only)
router.post('/:sessionId/games/:gameId/start-player-check', authenticateToken, (req, res) => {
  try {
    const { sessionId, gameId } = req.params;

    // Get the game to verify it exists and has a room code
    const game = db.prepare(`
      SELECT sg.*, g.max_players
      FROM session_games sg
      JOIN games g ON sg.game_id = g.id
      WHERE sg.session_id = ? AND sg.id = ?
    `).get(sessionId, gameId);

    if (!game) {
      return res.status(404).json({ error: 'Session game not found' });
    }

    if (!game.room_code) {
      return res.status(400).json({ error: 'Game does not have a room code' });
    }

    // Start room monitoring (will hand off to player count check when game starts)
    startMonitor(sessionId, gameId, game.room_code, game.max_players);

    res.json({ 
      message: 'Room monitor started',
      status: 'monitoring'
    });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// Stop player count check for a session game (admin only)
router.post('/:sessionId/games/:gameId/stop-player-check', authenticateToken, (req, res) => {
  try {
    const { sessionId, gameId } = req.params;

    // Stop both room monitor and player count check
    stopMonitor(sessionId, gameId);

    res.json({ 
      message: 'Room monitor and player count check stopped',
      status: 'stopped'
    });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// Manually update player count for a session game (admin only)
router.patch('/:sessionId/games/:gameId/player-count', authenticateToken, (req, res) => {
  try {
    const { sessionId, gameId } = req.params;
    const { player_count } = req.body;

    if (player_count === undefined || player_count === null) {
      return res.status(400).json({ error: 'player_count is required' });
    }

    const count = parseInt(player_count);
    if (isNaN(count) || count < 0) {
      return res.status(400).json({ error: 'player_count must be a positive number' });
    }

    // Update the player count
    const result = db.prepare(`
      UPDATE session_games 
      SET player_count = ?, player_count_check_status = 'completed'
      WHERE session_id = ? AND id = ?
    `).run(count, sessionId, gameId);

    if (result.changes === 0) {
      return res.status(404).json({ error: 'Session game not found' });
    }

    // Broadcast via WebSocket
    const wsManager = getWebSocketManager();
    if (wsManager) {
      wsManager.broadcastEvent('player-count.updated', {
        sessionId,
        gameId,
        playerCount: count,
        status: 'completed'
      }, parseInt(sessionId));
    }

    res.json({ 
      message: 'Player count updated successfully',
      player_count: count
    });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

module.exports = router;

