import { Application, Assets } from 'pixi.js'
import { Board } from '@/game/Board'
import { assets } from '@/game/utilities/assets'
import { Tile } from '@/game/Tile'
import { Hand } from '@/game/Hand'
import type { Movement, TileDto } from '@/common/interfaces'
import type { SocketIoClientService } from '@/services/SocketIoClientService'

export class Game {
  public board!: Board
  public hand!: Hand
  private app: Application = new Application()
  private selectedTile: TileDto | undefined

  constructor(
    private options: { boardScale: number; handScale: number; width: number; height: number } = {
      boardScale: 1,
      handScale: 1,
      width: 1200,
      height: 650
    },
    private emit: any,
    private socketService: SocketIoClientService,
    private playerId: string,
    private sessionId: string
  ) {}

  async setup(): Promise<HTMLCanvasElement> {
    const width = 1200
    const height = 650

    await this.app.init({ width, height })
    return this.app.canvas
  }

  start() {
    this.board = new Board(this.app)
    this.hand = new Hand(this.app)
    this.hand.scale = this.options.handScale
    this.board.scale = this.options.boardScale
    this.setBoardEvents()
    this.setHandEvents()
  }

  async preload() {
    await Assets.load(assets)
  }

  destroy() {
    this.removeBoardEvents()
    this.removeHandEvents()
    this.app.destroy()
  }

  private setHandEvents() {
    this.hand.on('handUpdated', (tiles: Tile[]) => {
      this.board.setPlayerHand(tiles)
    })
    this.hand.on('tileClick', (tile: TileDto) => {
      this.selectedTile = tile
      if (tile !== undefined) {
        this.board.setMovesForTile(this.getMoves(tile), tile)
      } else {
        this.board.cleanInteractions()
      }
    })

    this.hand.on('passClick', () => {
      const move: Movement = {
        id: '',
        type: 'pass',
        playerId: this.playerId
      }
      this.emit('move', move)
      this.board.updateBoard(move)
    })

    this.hand.on('nextClick', async () => {
      await this.socketService.sendMessageWithAck('playerReady', {
        user: this.playerId,
        sessionId: this.sessionId
      })
    })
  }

  getMoves(tile: any): [boolean, boolean] {
    if (tile === undefined) return [false, false]
    if (this.board.count === 0) return [false, true]

    const validEnds: [boolean, boolean] = [false, false]
    const freeEnds = this.board.freeEnds

    if (freeEnds !== undefined) {
      if (tile.pips != undefined) {
        validEnds[0] = tile.pips.includes(freeEnds[0])
        validEnds[1] = tile.pips.includes(freeEnds[1])
      }
    }
    return validEnds
  }

  public setCanMakeMove(value: boolean) {
    this.hand.canMove = value
    this.board.canMove = value
  }

  private setBoardEvents() {
    this.board.on('leftClick', (data) => {
      console.log('left data :>> ', data)
      if (this.selectedTile === undefined) return
      const move: Movement = {
        tile: this.selectedTile,
        type: 'left',
        playerId: this.playerId,
        ...data
      }
      this.emit('move', move)
      this.hand.tileMoved(this.selectedTile)
      this.board.updateBoard({ ...move, tile: this.selectedTile })
    })

    this.board.on('rightClick', (data) => {
      console.log('right data :>> ', data)
      if (this.selectedTile === undefined) return
      const move: Movement = {
        tile: this.selectedTile,
        type: 'right',
        playerId: this.playerId,
        ...data
      }
      this.emit('move', move)
      this.hand.tileMoved(this.selectedTile)
      this.board.updateBoard({ ...move, tile: this.selectedTile })
    })
  }

  gameFinished() {
    this.hand.gameFinished()
    this.board.gameFinished()
  }

  private removeBoardEvents() {
    this.board.off('leftClick')
    this.board.off('rightClick')
  }

  private removeHandEvents() {
    this.hand.off('tileClick')
    this.hand.off('passClick')
  }
}
