import { io, Socket } from 'socket.io-client'
import { SocketIoEventManager } from '@/managers/SocketIoEventManager'
import { useAuthStore } from '@/stores/auth'
import { storeToRefs } from 'pinia'
import { ServiceBase } from './ServiceBase'

export class SocketIoClientService extends ServiceBase {
  private socket!: Socket
  private isConnected = false
  private gameEventManager: SocketIoEventManager

  constructor(private url: string) {
    super()
    console.log('SOCKET: url :>> ', url)
    this.gameEventManager = new SocketIoEventManager(this)
  }

  async connect(): Promise<void> {
    const auth = useAuthStore()
    const { jwt, isLoggedIn } = storeToRefs(auth)
    return new Promise((resolve, reject) => {
      if (!isLoggedIn) {
        reject('Not logged in')
      }
      console.log('this.url :>> ', this.url)
      this.socket = io(this.url, {
        auth: {
          token: jwt.value,
        },
      })
      this.socket.on('connect', () => {
        if (this.socket && this.socket.recovered) {
          this.logger.debug('SOCKET: socket recovered succesfully')
        } else {
          this.logger.debug('SOCKET: socket connected')
        }
        this.isConnected = true
        this.addEvents()
        resolve()
      })
    })
  }

  async joinRoom(room: string): Promise<void> {
    if (this.isConnected) {
      this.socket.emit('client:join-room', room)
    } else {
      this.logger.debug('SOCKET: Not connected to server')
    }
  }

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

    this.socket.on('reconnect', () => {
      this.isConnected = true
      this.logger.debug('SOCKET: Reconnected to server')
    })

    this.socket.on('reconnect_error', () => {
      this.isConnected = false
      this.logger.debug('SOCKET: Failed to reconnect to server')
    })

    this.socket.on('ping', () => {
      this.socket.emit('pong') // Send pong response
    })

    // Custom events

    this.socket.on('server:game-event', (data: any) => {
      this.gameEventManager.handleGameEvent(data)
    })

    this.socket.on('server:game-event-ack', async (data: any, callback: any) => {
      await this.gameEventManager.handleGameEventAck(data, callback)
    })

    this.socket.onAny((eventName, eventData) => {
      if (eventName === 'server:game-event' || eventName === 'server:game-event-ack') {
        const { event, data } = eventData
        this.logger.trace(`SOCKET: Received event: ${event}`, data)
      }
    })
  }

  sendMessage(event: string, data: any): void {
    if (this.isConnected) {
      this.socket?.emit('client:event', { event, data })
      this.logger.trace(`SOCKET: sendMessage :>> ${event}`, data)
    } else {
      this.logger.trace('Not connected to server')
    }
  }

  async sendMessageWithAck(event: string, data: any): Promise<any> {
    if (this.isConnected) {
      this.logger.trace(`SOCKET: sendMessageWithAck :>> ${event}}`, data)
      return await this.socket?.emitWithAck('client:event-with-ack', { event, data })
    } else {
      this.logger.trace('SOCKET: Not connected to server')
    }
  }

  disconnect(): void {
    this.socket?.disconnect()
    this.isConnected = false
    this.logger.debug('SOCKET: Disconnected from server')
  }
}
