const db = require('../database/db');

/**
 * Nexus Integration Service
 * Handles cross-platform game sessions, friends, lobbies, and presence
 */
class NexusIntegrationService {

  /**
   * Start game session for player
   */
  async startGameSession(userId, nexusPlayerId, gameId, gameName, metadata = {}) {
    // Check if user already has active session
    const activeSessionResult = await db.query(
      `SELECT * FROM game_sessions 
       WHERE user_id = $1 AND session_state = 'active'`,
      [userId]
    );

    // End previous session if exists
    if (activeSessionResult.rows.length > 0) {
      const oldSession = activeSessionResult.rows[0];
      await this.endGameSession(oldSession.id, userId);
    }

    // Create new session
    const sessionResult = await db.query(
      `INSERT INTO game_sessions 
       (user_id, nexus_player_id, game_id, game_name, metadata)
       VALUES ($1, $2, $3, $4, $5)
       RETURNING *`,
      [userId, nexusPlayerId, gameId, gameName, JSON.stringify(metadata)]
    );

    const session = sessionResult.rows[0];

    // Update Nexus integration
    await db.query(
      `INSERT INTO nexus_integrations 
       (user_id, game_id, player_identity_id, in_game_name, online_in_game, last_game_session_at, current_game_session_id)
       VALUES ($1, $2, 
         (SELECT id FROM identities WHERE user_id = $1 AND context = 'player' LIMIT 1),
         $3, true, NOW(), $4)
       ON CONFLICT (user_id, game_id)
       DO UPDATE SET 
         online_in_game = true,
         last_game_session_at = NOW(),
         current_game_session_id = $4`,
      [userId, gameId, metadata.playerName || 'Player', session.id]
    );

    // Update user's overall status
    await db.query(
      `UPDATE users 
       SET status = 'online' 
       WHERE id = $1`,
      [userId]
    );

    // Get overlay config
    const overlayConfig = await this.getOverlayConfig(userId);

    return {
      session: session,
      overlayConfig: overlayConfig
    };
  }

  /**
   * Update game session state
   */
  async updateGameSession(sessionId, userId, state, metadata = {}) {
    // Get session
    const sessionResult = await db.query(
      `SELECT * FROM game_sessions WHERE id = $1 AND user_id = $2`,
      [sessionId, userId]
    );

    if (sessionResult.rows.length === 0) {
      throw new Error('Game session not found');
    }

    const session = sessionResult.rows[0];

    // Update metadata
    const currentMetadata = session.metadata || {};
    const updatedMetadata = { ...currentMetadata, ...metadata, state };

    await db.query(
      `UPDATE game_sessions 
       SET metadata = $2, session_state = $3
       WHERE id = $1`,
      [sessionId, JSON.stringify(updatedMetadata), state]
    );

    // Update Nexus integration game_state
    await db.query(
      `UPDATE nexus_integrations
       SET game_state = $2
       WHERE user_id = $1 AND game_id = $3`,
      [userId, JSON.stringify({ state, ...metadata }), session.game_id]
    );

    return {
      sessionId: sessionId,
      state: state,
      updatedAt: new Date()
    };
  }

  /**
   * End game session
   */
  async endGameSession(sessionId, userId, metadata = {}) {
    // Get session
    const sessionResult = await db.query(
      `SELECT * FROM game_sessions WHERE id = $1 AND user_id = $2`,
      [sessionId, userId]
    );

    if (sessionResult.rows.length === 0) {
      throw new Error('Game session not found');
    }

    const session = sessionResult.rows[0];

    // Calculate duration
    const duration = Math.floor((new Date() - new Date(session.started_at)) / 1000);

    // Update session
    const currentMetadata = session.metadata || {};
    await db.query(
      `UPDATE game_sessions 
       SET session_state = 'ended', 
           ended_at = NOW(), 
           duration_seconds = $2,
           metadata = $3
       WHERE id = $1`,
      [
        sessionId,
        duration,
        JSON.stringify({ ...currentMetadata, ...metadata })
      ]
    );

    // Update Nexus integration
    await db.query(
      `UPDATE nexus_integrations
       SET online_in_game = false,
           current_game_session_id = NULL
       WHERE user_id = $1 AND game_id = $2`,
      [userId, session.game_id]
    );

    return {
      sessionId: sessionId,
      duration: duration,
      endedAt: new Date()
    };
  }

  /**
   * Get overlay configuration for user
   */
  async getOverlayConfig(userId) {
    const result = await db.query(
      `SELECT overlay_enabled, overlay_position, auto_mute_enabled
       FROM nexus_integrations
       WHERE user_id = $1
       LIMIT 1`,
      [userId]
    );

    if (result.rows.length === 0) {
      // Default config
      return {
        enabled: true,
        position: 'top-right',
        opacity: 0.9,
        notifications: true,
        autoMute: true
      };
    }

    const config = result.rows[0];

    return {
      enabled: config.overlay_enabled !== false,
      position: config.overlay_position || 'top-right',
      opacity: 0.9,
      notifications: true,
      autoMute: config.auto_mute_enabled !== false
    };
  }

  /**
   * Create game lobby
   */
  async createGameLobby(hostUserId, gameId, maxPlayers, isPublic) {
    // Generate lobby code
    const lobbyCode = this.generateLobbyCode();

    // Create conversation for lobby
    const conversationResult = await db.query(
      `INSERT INTO conversations (type, name, created_by)
       VALUES ('group', $1, $2)
       RETURNING id`,
      [`Game Lobby - ${lobbyCode}`, hostUserId]
    );

    const conversationId = conversationResult.rows[0].id;

    // Create lobby
    const lobbyResult = await db.query(
      `INSERT INTO game_lobbies 
       (game_id, lobby_code, host_user_id, conversation_id, max_players, is_public)
       VALUES ($1, $2, $3, $4, $5, $6)
       RETURNING *`,
      [gameId, lobbyCode, hostUserId, conversationId, maxPlayers, isPublic]
    );

    const lobby = lobbyResult.rows[0];

    // Add host as participant
    await db.query(
      `INSERT INTO game_lobby_participants (lobby_id, user_id, ready)
       VALUES ($1, $2, true)`,
      [lobby.id, hostUserId]
    );

    // Add host to conversation
    const hostIdentity = await db.query(
      `SELECT id FROM identities WHERE user_id = $1 AND is_primary = true LIMIT 1`,
      [hostUserId]
    );

    if (hostIdentity.rows.length > 0) {
      await db.query(
        `INSERT INTO conversation_participants (conversation_id, user_id, identity_id)
         VALUES ($1, $2, $3)`,
        [conversationId, hostUserId, hostIdentity.rows[0].id]
      );
    }

    return {
      id: lobby.id,
      gameId: lobby.game_id,
      lobbyCode: lobby.lobby_code,
      conversationId: conversationId,
      maxPlayers: lobby.max_players,
      isPublic: lobby.is_public,
      status: lobby.status,
      createdAt: lobby.created_at
    };
  }

  /**
   * Join game lobby
   */
  async joinGameLobby(lobbyId, userId, lobbyCode = null) {
    // Get lobby
    let lobbyResult;

    if (lobbyCode) {
      lobbyResult = await db.query(
        `SELECT * FROM game_lobbies WHERE lobby_code = $1 AND status = 'open'`,
        [lobbyCode]
      );
    } else {
      lobbyResult = await db.query(
        `SELECT * FROM game_lobbies WHERE id = $1 AND status = 'open'`,
        [lobbyId]
      );
    }

    if (lobbyResult.rows.length === 0) {
      throw new Error('Lobby not found or not open');
    }

    const lobby = lobbyResult.rows[0];

    // Check if lobby is full
    const participantCount = await db.query(
      `SELECT COUNT(*) as count FROM game_lobby_participants 
       WHERE lobby_id = $1 AND left_at IS NULL`,
      [lobby.id]
    );

    if (parseInt(participantCount.rows[0].count) >= lobby.max_players) {
      throw new Error('Lobby is full');
    }

    // Add participant
    await db.query(
      `INSERT INTO game_lobby_participants (lobby_id, user_id)
       VALUES ($1, $2)
       ON CONFLICT (lobby_id, user_id) 
       DO UPDATE SET left_at = NULL, joined_at = NOW()`,
      [lobby.id, userId]
    );

    // Add to conversation
    const userIdentity = await db.query(
      `SELECT id FROM identities WHERE user_id = $1 AND is_primary = true LIMIT 1`,
      [userId]
    );

    if (userIdentity.rows.length > 0) {
      await db.query(
        `INSERT INTO conversation_participants (conversation_id, user_id, identity_id)
         VALUES ($1, $2, $3)
         ON CONFLICT (conversation_id, user_id) DO NOTHING`,
        [lobby.conversation_id, userId, userIdentity.rows[0].id]
      );
    }

    // Get all participants
    const participants = await db.query(
      `SELECT u.id, u.username, u.avatar_url, lp.ready, lp.team_id
       FROM game_lobby_participants lp
       JOIN users u ON lp.user_id = u.id
       WHERE lp.lobby_id = $1 AND lp.left_at IS NULL`,
      [lobby.id]
    );

    return {
      id: lobby.id,
      participants: participants.rows.map(p => ({
        userId: p.id,
        username: p.username,
        avatar: p.avatar_url,
        ready: p.ready,
        teamId: p.team_id
      })),
      conversationId: lobby.conversation_id
    };
  }

  /**
   * Toggle ready status
   */
  async toggleReady(lobbyId, userId) {
    // Get current ready state
    const result = await db.query(
      `SELECT ready FROM game_lobby_participants 
       WHERE lobby_id = $1 AND user_id = $2 AND left_at IS NULL`,
      [lobbyId, userId]
    );

    if (result.rows.length === 0) {
      throw new Error('Not in lobby');
    }

    const newReady = !result.rows[0].ready;

    // Update
    await db.query(
      `UPDATE game_lobby_participants 
       SET ready = $3 
       WHERE lobby_id = $1 AND user_id = $2`,
      [lobbyId, userId, newReady]
    );

    return newReady;
  }

  /**
   * Start game (host only)
   */
  async startLobbyGame(lobbyId, hostUserId) {
    // Verify host
    const lobbyResult = await db.query(
      `SELECT * FROM game_lobbies WHERE id = $1 AND host_user_id = $2`,
      [lobbyId, hostUserId]
    );

    if (lobbyResult.rows.length === 0) {
      throw new Error('Not authorized or lobby not found');
    }

    // Check if all players are ready
    const notReady = await db.query(
      `SELECT COUNT(*) as count FROM game_lobby_participants 
       WHERE lobby_id = $1 AND left_at IS NULL AND ready = false`,
      [lobbyId]
    );

    if (parseInt(notReady.rows[0].count) > 0) {
      throw new Error('Not all players are ready');
    }

    // Update lobby status
    await db.query(
      `UPDATE game_lobbies 
       SET status = 'in-progress', started_at = NOW() 
       WHERE id = $1`,
      [lobbyId]
    );

    return {
      id: lobbyId,
      status: 'in-progress',
      startedAt: new Date()
    };
  }

  /**
   * Leave lobby
   */
  async leaveLobby(lobbyId, userId) {
    await db.query(
      `UPDATE game_lobby_participants 
       SET left_at = NOW() 
       WHERE lobby_id = $1 AND user_id = $2`,
      [lobbyId, userId]
    );

    return { success: true };
  }

  /**
   * Send friend request
   */
  async sendFriendRequest(fromUserId, targetIdentifier) {
    // Find target user by identifier
    const targetResult = await db.query(
      `SELECT user_id FROM identities WHERE identifier = $1`,
      [targetIdentifier]
    );

    if (targetResult.rows.length === 0) {
      throw new Error('User not found');
    }

    const toUserId = targetResult.rows[0].user_id;

    if (fromUserId === toUserId) {
      throw new Error('Cannot send friend request to yourself');
    }

    // Check if already friends
    const user1 = fromUserId < toUserId ? fromUserId : toUserId;
    const user2 = fromUserId < toUserId ? toUserId : fromUserId;

    const friendshipCheck = await db.query(
      `SELECT * FROM friendships 
       WHERE user1_id = $1 AND user2_id = $2`,
      [user1, user2]
    );

    if (friendshipCheck.rows.length > 0) {
      throw new Error('Already friends');
    }

    // Check for existing request
    const existingRequest = await db.query(
      `SELECT * FROM friend_requests 
       WHERE from_user_id = $1 AND to_user_id = $2 AND status = 'pending'`,
      [fromUserId, toUserId]
    );

    if (existingRequest.rows.length > 0) {
      throw new Error('Friend request already sent');
    }

    // Create request
    const requestResult = await db.query(
      `INSERT INTO friend_requests (from_user_id, to_user_id)
       VALUES ($1, $2)
       RETURNING *`,
      [fromUserId, toUserId]
    );

    return requestResult.rows[0];
  }

  /**
   * Respond to friend request
   */
  async respondToFriendRequest(requestId, userId, accept) {
    // Get request
    const requestResult = await db.query(
      `SELECT * FROM friend_requests WHERE id = $1 AND to_user_id = $2 AND status = 'pending'`,
      [requestId, userId]
    );

    if (requestResult.rows.length === 0) {
      throw new Error('Friend request not found');
    }

    const request = requestResult.rows[0];

    if (accept) {
      // Create friendship
      const user1 = request.from_user_id < userId ? request.from_user_id : userId;
      const user2 = request.from_user_id < userId ? userId : request.from_user_id;

      await db.query(
        `INSERT INTO friendships (user1_id, user2_id)
         VALUES ($1, $2)
         ON CONFLICT (user1_id, user2_id) DO NOTHING`,
        [user1, user2]
      );

      // Update request
      await db.query(
        `UPDATE friend_requests 
         SET status = 'accepted', responded_at = NOW() 
         WHERE id = $1`,
        [requestId]
      );

      // Get friend info
      const friendInfo = await db.query(
        `SELECT id, username FROM users WHERE id = $1`,
        [request.from_user_id]
      );

      return { 
        accepted: true,
        friendship: {
          id: `${user1}-${user2}`,
          friendUserId: request.from_user_id,
          friendUsername: friendInfo.rows[0]?.username,
          createdAt: new Date()
        }
      };
    } else {
      // Reject
      await db.query(
        `UPDATE friend_requests 
         SET status = 'rejected', responded_at = NOW() 
         WHERE id = $1`,
        [requestId]
      );

      return { accepted: false };
    }
  }

  /**
   * Get pending friend requests
   */
  async getPendingFriendRequests(userId) {
    const result = await db.query(
      `SELECT fr.id, fr.from_user_id, fr.created_at, u.username, u.avatar_url, i.identifier
       FROM friend_requests fr
       JOIN users u ON fr.from_user_id = u.id
       LEFT JOIN identities i ON i.user_id = u.id AND i.is_primary = true
       WHERE fr.to_user_id = $1 AND fr.status = 'pending'
       ORDER BY fr.created_at DESC`,
      [userId]
    );

    return result.rows.map(row => ({
      id: row.id,
      fromUserId: row.from_user_id,
      username: row.username,
      identifier: row.identifier,
      avatar: row.avatar_url,
      createdAt: row.created_at
    }));
  }

  /**
   * Remove friend
   */
  async removeFriend(userId, friendUserId) {
    const user1 = userId < friendUserId ? userId : friendUserId;
    const user2 = userId < friendUserId ? friendUserId : userId;

    await db.query(
      `DELETE FROM friendships WHERE user1_id = $1 AND user2_id = $2`,
      [user1, user2]
    );

    return { success: true };
  }

  /**
   * Get friends with current status
   */
  async getFriends(userId) {
    const result = await db.query(
      `SELECT 
         u.id, u.username, u.avatar_url, u.status, u.last_seen_at,
         i.identifier,
         gs.game_id, gs.game_name, gs.session_state, gs.started_at,
         ni.game_state
       FROM friendships f
       JOIN users u ON (u.id = f.user1_id OR u.id = f.user2_id) AND u.id != $1
       LEFT JOIN identities i ON i.user_id = u.id AND i.context = 'player'
       LEFT JOIN game_sessions gs ON gs.user_id = u.id AND gs.session_state = 'active'
       LEFT JOIN nexus_integrations ni ON ni.user_id = u.id AND ni.current_game_session_id = gs.id
       WHERE f.user1_id = $1 OR f.user2_id = $1
       ORDER BY u.status DESC, u.last_seen_at DESC`,
      [userId]
    );

    const friends = result.rows.map(row => {
      const friend = {
        userId: row.id,
        username: row.username,
        identifier: row.identifier,
        avatar: row.avatar_url,
        status: row.status || 'offline',
        lastSeen: row.last_seen_at
      };

      if (row.game_id) {
        const gameState = row.game_state || {};
        friend.currentGame = {
          gameId: row.game_id,
          gameName: row.game_name,
          state: gameState.state || 'in-menu',
          joinable: gameState.state !== 'in-match',
          sessionStarted: row.started_at
        };
      }

      return friend;
    });

    return {
      friends: friends,
      online: friends.filter(f => f.status === 'online').length,
      total: friends.length
    };
  }

  /**
   * Helper: Generate random lobby code
   */
  generateLobbyCode() {
    const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';
    let code = '';
    for (let i = 0; i < 8; i++) {
      code += chars.charAt(Math.floor(Math.random() * chars.length));
    }
    return code;
  }
}

module.exports = new NexusIntegrationService();
