import type { MatchSessionState, GameState, PlayerDto } from '@/common/interfaces'
import { io, Socket } from 'socket.io-client'
import { SocketIoEventManager } from '@/managers/SocketIoEventManager'
import { LoggingService } from './LoggingService'

export class SocketIoClientService {
  public socket: Socket
  private isConnected = false
  private gameEventManager = new SocketIoEventManager()
  private logger: LoggingService = new LoggingService()

  constructor(url: string) {
    this.socket = io(url)
    this.addEvents()
  }

  addEvents(): void {
    this.socket.on('connect', () => {
      this.isConnected = true
      if (this.socket && this.socket.recovered) {
        console.log('socket recovered succesfully')
      } else {
        console.log('socket connected')
      }
    })

    this.socket.on('disconnect', () => {
      this.isConnected = false
      console.log('Disconnected from server')
    })

    this.socket.on('reconnect', () => {
      this.isConnected = true
      console.log('Reconnected to server')
    })

    this.socket.on('reconnect_error', () => {
      this.isConnected = false
      console.log('Failed to reconnect to server')
    })

    this.socket.on('matchState', (data: MatchSessionState, callback: any) => {
      callback(this.gameEventManager.handleSessionStateEvent(data))
    })

    this.socket.on('gameState', (data: GameState, callback: any) => {
      callback(this.gameEventManager.handleGameStateEvent(data))
    })

    this.socket.on('playerState', (data: PlayerDto, callback: any) => {
      this.logger.debug('playerState', data)
      callback(this.gameEventManager.handlePlayerStateEvent(data))
    })

    this.socket.on('makeMove', async (data: any, callback: any) => {
      callback(await this.gameEventManager.handleCanMakeMoveEvent(data))
    })

    this.socket.on('chooseTile', async (data: any, callback: any) => {
      callback(await this.gameEventManager.handleCanSelectTileEvent())
    })

    this.socket.on('game-finished', () => {
      this.logger.debug('game-finished event received')
      this.gameEventManager.handleGameFinishedEvent()
    })

    this.socket.on('ping', () => {
      console.log('Ping received from server')
      this.socket.emit('pong') // Send pong response
    })
  }

  sendMessage(event: string, data: any): void {
    if (this.isConnected) {
      this.socket?.emit(event, data)
    } else {
      console.log('Not connected to server')
    }
  }

  async sendMessageWithAck(event: string, data: any): Promise<any> {
    if (this.isConnected) {
      return await this.socket?.emitWithAck(event, data)
    } else {
      console.log('Not connected to server')
    }
  }

  disconnect(): void {
    this.socket?.disconnect()
    this.isConnected = false
    console.log('Disconnected from server')
  }
}
