import { Client, Session } from '@heroiclabs/nakama-js';
import type { Socket } from '@heroiclabs/nakama-js';

// Game token info received from backend
export interface GameTokenInfo {
  game_token: string;
  expires_at: string;
  nakama_server: string;
  nakama_key: string;
  remaining_times: number;
}

class NakamaManager {
  private client: Client | null = null;
  private session: Session | null = null;
  private socket: Socket | null = null;
  private useSSL: boolean = false;
  private gameToken: string | null = null;

  /**
   * Initialize the Nakama client with server info from backend
   * @param serverUrl WebSocket URL (e.g., "wss://nakama.example.com" or "ws://localhost:7350")
   * @param serverKey Server key (default: "defaultkey")
   */
  initClient(serverUrl: string, serverKey: string = 'defaultkey') {
    // Parse server URL
    const url = new URL(serverUrl.replace('wss://', 'https://').replace('ws://', 'http://'));
    const host = url.hostname;
    const port = url.port || (serverUrl.startsWith('wss') ? '443' : '7350');
    this.useSSL = serverUrl.startsWith('wss');

    console.log(`Initializing Nakama client: ${this.useSSL ? 'wss' : 'ws'}://${host}:${port}`);
    this.client = new Client(serverKey, host, port, this.useSSL);
  }

  /**
   * Authenticate with a game token from the backend
   * @param gameToken JWT game token from /api/app/game/enter
   */
  async authenticateWithGameToken(gameToken: string) {
    if (!this.client) {
      throw new Error('Client not initialized. Call initClient() first.');
    }

    // Return existing session if already authenticated with same token
    if (this.session && this.socket && this.gameToken === gameToken) {
      console.log('Already authenticated with this token, returning existing session');
      return this.session;
    }

    this.gameToken = gameToken;

    // Use authenticateCustom with the game token as the custom ID
    // This creates a unique Nakama user per game token
    // The actual user verification happens in MatchJoinAttempt on the server
    const customId = `game_${Date.now()}_${Math.random().toString(36).substring(7)}`;

    console.log('Authenticating with game token...');
    this.session = await this.client.authenticateCustom(customId, true);

    if (this.session.user_id) {
      console.log('Nakama User ID:', this.session.user_id);
    }

    // Connect Socket
    this.socket = this.client.createSocket(this.useSSL, false);
    await this.socket.connect(this.session, true);
    console.log('Socket connected');

    return this.session;
  }

  /**
   * Find a match using matchmaker
   * The game_token is passed as metadata for server-side validation
   */
  async findMatch(minCount: number = 2, maxCount: number = 2) {
    if (!this.socket) throw new Error('Socket not connected');
    if (!this.gameToken) throw new Error('No game token set');

    const query = '*';
    // Pass game_token as string properties for matchmaker
    const stringProperties = { game_token: this.gameToken };

    console.log(`Searching for match: ${minCount}-${maxCount} players`);
    return this.socket.addMatchmaker(query, minCount, maxCount, stringProperties);
  }

  /**
   * Join a match with the game token
   * @param matchId Match ID from matchmaker
   * @param token Matchmaker token (if from matchmaker)
   */
  async joinMatch(matchId: string, token?: string) {
    if (!this.socket) throw new Error('Socket not connected');
    if (!this.gameToken) throw new Error('No game token set');

    // Pass game_token in metadata for server validation
    const metadata = { game_token: this.gameToken };

    return this.socket.joinMatch(matchId, token, metadata);
  }

  async sendMatchState(matchId: string, opCode: number, data: string) {
    if (!this.socket) throw new Error('Socket not connected');
    return this.socket.sendMatchState(matchId, opCode, data);
  }

  getSocket() {
    return this.socket;
  }

  getSession() {
    return this.session;
  }

  getGameToken() {
    return this.gameToken;
  }

  /**
   * Disconnect and cleanup
   */
  async disconnect() {
    if (this.socket) {
      await this.socket.disconnect(false);
      this.socket = null;
    }
    this.session = null;
    this.gameToken = null;
  }
}

export const nakamaManager = new NakamaManager();
