import {
  Application,
  Container,
  EventEmitter,
  Graphics,
  Sprite,
  Text,
  Texture,
  Ticker
} from 'pixi.js'
import { Tile } from '@/game/Tile'
import type { Dimension, PlayerDto, TileDto } from '@/common/interfaces'
import { GlowFilter } from 'pixi-filters'
import { Scale, type ScaleFunction } from './utilities/scale'
import { LoggingService } from '@/services/LoggingService'

export class Hand extends EventEmitter {
  tiles: Tile[] = []
  container: Container = new Container()
  buttonPass: Container = new Container()
  buttonNext: Container = new Container()
  height: number
  width: number
  ticker: Ticker
  lastTimeClicked: number = 0
  doubleClickThreshold: number = 300
  initialized: boolean = false
  _canMove: boolean = false
  scale: number = 1
  scaleY!: ScaleFunction
  scaleX!: ScaleFunction
  grain: number = 25
  logger: LoggingService = new LoggingService()

  constructor(app: Application) {
    super()
    app.stage.addChild(this.container)
    this.ticker = app.ticker
    this.height = 130
    this.width = app.canvas.width
    this.container.y = app.canvas.height - this.height
    this.container.width = this.width
    this.container.height = this.height
    this.calculateScale()
    this.addBg()
  }

  gameFinished() {
    this.logger.debug('gameFinished')
    this.tiles = []
    this.container.removeChildren()
    this.initialized = false
    this.buttonNext = this.createButton(
      'NEXT',
      { x: this.width / 2 - 25, y: this.height / 2, width: 50, height: 20 },
      'nextClick'
    )
  }

  get canMove() {
    return this._canMove
  }

  private calculateScale() {
    const scaleXSteps = Math.floor(this.width / (this.grain * this.scale)) / 2
    const scaleYSteps = Math.floor(this.height / (this.grain * this.scale)) / 2
    this.scaleX = Scale([-scaleXSteps, scaleXSteps], [0, this.width])
    this.scaleY = Scale([-scaleYSteps, scaleYSteps], [0, this.height])
  }

  set canMove(value: boolean) {
    this._canMove = value
    if (value) {
      this.createPassButton()
    } else {
      this.container.removeChild(this.buttonPass)
    }

    this.tiles.forEach((tile) => {
      tile.interactive = value
    })
  }

  initialize(playerState: PlayerDto) {
    this.tiles = this.createTiles(playerState)
    this.emit('handUpdated', this.tiles)
    this.initialized = this.tiles.length > 0
    this.renderTiles()
  }

  private addBg() {
    const bg = new Sprite(Texture.WHITE)
    bg.alpha = 0.08
    bg.width = this.width
    bg.height = this.height
    this.container.addChild(bg)
  }

  private onTileClick(tile: Tile) {
    // if (Date.now() - this.lastTimeClicked < this.doubleClickThreshold) {
    //   this.emit('tileDoubleClick', { id: tile.id })
    //   return
    // }

    const selected = this.tiles.find((t) => t.selected)
    if (selected) {
      this.deselectTile(selected)
      if (selected.id === tile.id) {
        this.emit('tileClick')
        return
      }
    }

    tile.selected = true
    tile.alpha = 1

    tile.animateTo(tile.x, tile.y - 10)
    this.emit('tileClick', tile.toPlain())
  }

  private deselectTile(selected: Tile) {
    selected.animateTo(selected.x, selected.y + 10)
    selected.selected = false
    selected.alpha = 0.7
  }

  public tileMoved(tileDto: TileDto) {
    const tile = this.tiles.find((t) => t.id === tileDto.id)

    if (!tile) return

    tile.interactive = false
    tile.clearFilters()
    tile.off('pointerdown')
    tile.off('pointerover')
    tile.off('pointerout')
  }

  private createButton(
    textStr: string,
    dimension: Dimension,
    action: string | Function
  ): Container {
    const { x, y, width, height } = dimension
    const rectangle = new Graphics().roundRect(x, y, width + 4, height + 4, 5).fill(0xffff00)
    const text = new Text({
      text: textStr,
      style: {
        fontFamily: 'Arial',
        fontSize: 12,
        fontWeight: 'bold',
        fill: 0x121212,
        align: 'center'
      }
    })
    text.anchor = 0.5
    const container = new Container()
    container.addChild(rectangle)
    container.addChild(text)

    text.y = y + height / 2
    text.x = x + width / 2

    container.eventMode = 'static'
    container.cursor = 'pointer'
    rectangle.alpha = 0.7
    text.alpha = 0.7
    container.on('pointerdown', () => {
      action instanceof Function ? action() : this.emit(action)
    })
    container.on('pointerover', () => {
      rectangle.alpha = 1
      text.alpha = 1
    })

    container.on('pointerout', () => {
      rectangle.alpha = 0.7
      text.alpha = 0.7
    })

    this.container.addChild(container)
    return container
  }

  private createPassButton() {
    const lastTile = this.tiles[this.tiles.length - 1]
    const x = lastTile ? lastTile.x + lastTile.width : this.scaleX(0)
    this.buttonPass = this.createButton(
      'PASS',
      { x, y: this.height / 2, width: 50, height: 20 },
      'passClick'
    )
  }

  update(playerState: PlayerDto) {
    if (!this.initialized) {
      this.initialize(playerState)
      return
    }
    const missing: Tile | undefined = this.tiles.find(
      (tile: Tile) => !playerState.hand.find((t) => t.id === tile.id)
    )
    if (missing) {
      this.container.removeChild(missing.getSprite())
      this.tiles = this.tiles.filter((tile) => tile.id !== missing.id)
      this.emit('handUpdated', this.tiles)
    }
    this.renderTiles()
  }

  private createTiles(playerState: PlayerDto) {
    return playerState.hand.map((tile) => {
      const newTile: Tile = new Tile(tile.id, this.ticker, tile.pips, this.scale)
      newTile.alpha = 0.7
      newTile.anchor = 0.5
      newTile.addTo(this.container)
      newTile.on('pointerdown', () => this.onTileClick(newTile))
      newTile.on('pointerover', () => {
        newTile.alpha = 1
        newTile.setFilters([
          new GlowFilter({
            distance: 10,
            outerStrength: 2,
            innerStrength: 1,
            color: 0xffffff,
            quality: 0.5
          })
        ])
      })
      newTile.on('pointerout', () => {
        if (!newTile.selected) {
          newTile.alpha = 0.7
          newTile.getSprite().filters = []
        }
      })
      return newTile
    })
  }

  renderTiles() {
    const deltaX = this.width / 2 - (this.tiles.length * 50 - 5) / 2
    this.tiles.forEach((tile, i) => {
      tile.setPosition(deltaX + tile.width / 2 + i * (tile.width + 5), tile.height / 2 + 20)
    })
  }
}
