import Phaser from 'phaser'
import type { ActionDifficulty } from '../actionMode'
import type { PlayerClass } from '../claudeCraftTypes'
import type { TalentModifiers } from '../claudeCraftTalents'
import { ACTION_ENEMY_SPRITES } from '../actionAssets'
import {
  type ActionDungeonId,
  type ActionRunMode,
  createBulldromeState,
  getAllEnemies,
  getTargetableUnits,
  updateBulldromeState,
  type BulldromeState,
  type EnemyState,
  type HealTextEvent,
  type NoticeTextEvent,
  type SpellSlot,
  type TargetableState,
} from './actionCombatSimulation'
import type { BossInput } from '../actionCombatTypes'

type SceneCallbacks = {
  difficulty: ActionDifficulty
  dungeonId: ActionDungeonId
  playerClassId?: PlayerClass
  playerTalentModifiers?: TalentModifiers
  runMode: ActionRunMode
  onStateChange: (state: BulldromeState) => void
}

type KeyMap = {
  W: Phaser.Input.Keyboard.Key
  A: Phaser.Input.Keyboard.Key
  S: Phaser.Input.Keyboard.Key
  D: Phaser.Input.Keyboard.Key
  R: Phaser.Input.Keyboard.Key
  ONE: Phaser.Input.Keyboard.Key
  TWO: Phaser.Input.Keyboard.Key
  THREE: Phaser.Input.Keyboard.Key
  FOUR: Phaser.Input.Keyboard.Key
  FIVE: Phaser.Input.Keyboard.Key
  SIX: Phaser.Input.Keyboard.Key
  SEVEN: Phaser.Input.Keyboard.Key
  EIGHT: Phaser.Input.Keyboard.Key
  NINE: Phaser.Input.Keyboard.Key
  ZERO: Phaser.Input.Keyboard.Key
}

export class BulldromeScene extends Phaser.Scene {
  private callbacks: SceneCallbacks
  private state: BulldromeState
  private keys: KeyMap | null = null
  private arenaGraphics?: Phaser.GameObjects.Graphics
  private telegraphGraphics?: Phaser.GameObjects.Graphics
  private hazardGraphics?: Phaser.GameObjects.Graphics
  private bossGraphics?: Phaser.GameObjects.Graphics
  private partyGraphics?: Phaser.GameObjects.Graphics
  private playerGraphics?: Phaser.GameObjects.Graphics
  private fxGraphics?: Phaser.GameObjects.Graphics
  private statusText?: Phaser.GameObjects.Text
  private enemySprites = new Map<string, Phaser.GameObjects.Image>()
  private lastResetDown = false
  private lastOneDown = false
  private lastTwoDown = false
  private lastThreeDown = false
  private lastFourDown = false
  private lastFiveDown = false
  private lastSixDown = false
  private lastSevenDown = false
  private lastEightDown = false
  private lastNineDown = false
  private lastZeroDown = false
  private queuedTargetId: string | null = null
  private queuedSpell: SpellSlot | null = null
  private hudPublishTimer = 0
  private seenHealEventIds = new Set<string>()
  private seenNoticeEventIds = new Set<string>()

  constructor(callbacks: SceneCallbacks) {
    super('BulldromeScene')
    this.callbacks = callbacks
    this.state = createBulldromeState(callbacks.difficulty, callbacks.dungeonId, callbacks.runMode, callbacks.playerClassId, callbacks.playerTalentModifiers)
  }

  preload() {
    for (const [kind, asset] of Object.entries(ACTION_ENEMY_SPRITES)) {
      this.load.image(`enemy-${kind}`, asset.src)
    }
  }

  create() {
    this.keys = this.input.keyboard?.addKeys('W,A,S,D,R,ONE,TWO,THREE,FOUR,FIVE,SIX,SEVEN,EIGHT,NINE,ZERO') as KeyMap
    this.arenaGraphics = this.add.graphics()
    this.telegraphGraphics = this.add.graphics()
    this.hazardGraphics = this.add.graphics()
    this.bossGraphics = this.add.graphics()
    this.partyGraphics = this.add.graphics()
    this.playerGraphics = this.add.graphics()
    this.fxGraphics = this.add.graphics()
    this.statusText = this.add.text(238, 58, '', {
      color: '#f4eed8',
      fontFamily: 'monospace',
      fontSize: '16px',
    })
    this.input.on('pointerdown', (pointer: Phaser.Input.Pointer) => {
      this.selectTargetAt(pointer.worldX, pointer.worldY)
    })
    this.cameras.main.setRoundPixels(true)
    this.callbacks.onStateChange(this.state)
  }

  selectTarget(targetId: string) {
    this.queuedTargetId = targetId
  }

  castSpell(slot: SpellSlot) {
    this.queuedSpell = slot
  }

  override update(_time: number, delta: number) {
    const input = this.readInput()
    this.state = updateBulldromeState(this.state, input, Math.min(delta / 1000, 0.05))
    if (input.reset) {
      this.seenHealEventIds.clear()
      this.seenNoticeEventIds.clear()
    }
    this.spawnHealTexts()
    this.spawnNoticeTexts()
    this.renderState()

    this.hudPublishTimer -= delta
    if (this.hudPublishTimer <= 0 || this.state.result !== 'playing' || this.state.lastHit || this.state.noticeEvents.length > 0) {
      this.callbacks.onStateChange(this.state)
      this.hudPublishTimer = 80
    }
  }

  private readInput(): BossInput {
    const keys = this.keys
    if (!keys) {
      return {
        xAxis: 0,
        yAxis: 0,
        reset: false,
        targetDelta: 0,
        targetId: this.consumeQueuedTarget(),
        castSpell: this.consumeQueuedSpell(),
      }
    }

    const resetDown = keys.R.isDown
    const oneDown = keys.ONE.isDown
    const twoDown = keys.TWO.isDown
    const threeDown = keys.THREE.isDown
    const fourDown = keys.FOUR.isDown
    const fiveDown = keys.FIVE.isDown
    const sixDown = keys.SIX.isDown
    const sevenDown = keys.SEVEN.isDown
    const eightDown = keys.EIGHT.isDown
    const nineDown = keys.NINE.isDown
    const zeroDown = keys.ZERO.isDown
    const pressedSpell = this.consumeQueuedSpell()
      ?? (oneDown && !this.lastOneDown ? 1
        : twoDown && !this.lastTwoDown ? 2
          : threeDown && !this.lastThreeDown ? 3
            : fourDown && !this.lastFourDown ? 4
              : fiveDown && !this.lastFiveDown ? 5
                : sixDown && !this.lastSixDown ? 6
                  : sevenDown && !this.lastSevenDown ? 7
                    : eightDown && !this.lastEightDown ? 8
                      : nineDown && !this.lastNineDown ? 9
                        : zeroDown && !this.lastZeroDown ? 10
                          : null)
    const input = {
      xAxis: Number(keys.D.isDown) - Number(keys.A.isDown),
      yAxis: Number(keys.S.isDown) - Number(keys.W.isDown),
      reset: resetDown && !this.lastResetDown,
      targetDelta: 0 as const,
      targetId: this.consumeQueuedTarget(),
      castSpell: pressedSpell,
    }
    this.lastResetDown = resetDown
    this.lastOneDown = oneDown
    this.lastTwoDown = twoDown
    this.lastThreeDown = threeDown
    this.lastFourDown = fourDown
    this.lastFiveDown = fiveDown
    this.lastSixDown = sixDown
    this.lastSevenDown = sevenDown
    this.lastEightDown = eightDown
    this.lastNineDown = nineDown
    this.lastZeroDown = zeroDown
    return input
  }

  private renderState() {
    this.drawArena()
    this.drawHazards()
    this.drawTelegraph()
    this.drawBoss()
    this.drawParty()
    this.drawPlayer()
    this.drawFx()
    this.statusText?.setText(this.state.message)
  }

  private spawnHealTexts() {
    for (const event of this.state.healEvents) {
      if (this.seenHealEventIds.has(event.id)) continue
      this.seenHealEventIds.add(event.id)
      this.spawnHealText(event)
    }
  }

  private spawnHealText(event: HealTextEvent) {
    const target = getTargetableUnits(this.state).find((unit) => unit.id === event.targetId)
    if (!target) return

    const ring = this.add.graphics()
    ring.setPosition(target.x, target.y)
    ring.lineStyle(4, 0x7dff9d, 0.95)
    ring.strokeCircle(0, 0, target.radius + 10)
    ring.lineStyle(2, 0xf4fff2, 0.8)
    ring.strokeCircle(0, 0, target.radius + 18)
    this.tweens.add({
      targets: ring,
      alpha: 0,
      scaleX: 1.45,
      scaleY: 1.45,
      duration: 520,
      ease: 'Cubic.easeOut',
      onComplete: () => ring.destroy(),
    })

    if (event.amount <= 0) return

    const text = this.add.text(target.x, target.y - target.radius - 18, `+${Math.ceil(event.amount)}`, {
      color: '#7dff9d',
      fontFamily: 'monospace',
      fontSize: '18px',
      fontStyle: 'bold',
      stroke: '#07110b',
      strokeThickness: 4,
    })
    text.setOrigin(0.5)
    this.tweens.add({
      targets: text,
      y: text.y - 30,
      alpha: 0,
      duration: 850,
      ease: 'Cubic.easeOut',
      onComplete: () => text.destroy(),
    })
  }

  private spawnNoticeTexts() {
    for (const event of this.state.noticeEvents) {
      if (this.seenNoticeEventIds.has(event.id)) continue
      this.seenNoticeEventIds.add(event.id)
      this.spawnNoticeText(event)
    }
  }

  private spawnNoticeText(event: NoticeTextEvent) {
    const target = getTargetableUnits(this.state).find((unit) => unit.id === event.targetId) ?? this.state.player
    const text = this.add.text(target.x, target.y - target.radius - 24, event.text, {
      color: '#ffd36d',
      fontFamily: 'monospace',
      fontSize: '16px',
      fontStyle: 'bold',
      stroke: '#120b03',
      strokeThickness: 4,
    })
    text.setOrigin(0.5)
    this.tweens.add({
      targets: text,
      y: text.y - 24,
      alpha: 0,
      duration: 950,
      ease: 'Cubic.easeOut',
      onComplete: () => text.destroy(),
    })
  }

  private consumeQueuedTarget() {
    const targetId = this.queuedTargetId
    this.queuedTargetId = null
    return targetId
  }

  private consumeQueuedSpell() {
    const spell = this.queuedSpell
    this.queuedSpell = null
    return spell
  }

  private selectTargetAt(x: number, y: number) {
    const units = [this.state.player, ...this.state.party, ...getAllEnemies(this.state)].filter((unit) => unit.hp > 0)
    const target = units
      .map((unit) => ({ unit, distance: Phaser.Math.Distance.Between(x, y, unit.x, unit.y) }))
      .filter(({ unit, distance }) => distance <= unit.radius + 14)
      .sort((a, b) => a.distance - b.distance)[0]?.unit
    if (target) this.selectTarget(target.id)
  }

  private drawArena() {
    const graphics = this.arenaGraphics
    if (!graphics) return

    const { width, height, padding } = this.state.arena
    graphics.clear()
    graphics.fillStyle(0x11151c, 1)
    graphics.fillRect(0, 0, width, height)

    graphics.fillStyle(0x18202a, 1)
    graphics.fillRect(padding, padding, width - padding * 2, height - padding * 2)

    graphics.lineStyle(3, 0x565066, 1)
    graphics.strokeRect(padding, padding, width - padding * 2, height - padding * 2)

    graphics.lineStyle(1, 0x252b35, 0.65)
    for (let x = padding + 48; x < width - padding; x += 48) {
      graphics.lineBetween(x, padding, x, height - padding)
    }
    for (let y = padding + 48; y < height - padding; y += 48) {
      graphics.lineBetween(padding, y, width - padding, y)
    }
  }

  private drawTelegraph() {
    const graphics = this.telegraphGraphics
    if (!graphics) return

    graphics.clear()
    for (const enemy of getAllEnemies(this.state)) {
      if (enemy.hp <= 0) continue
      const telegraph = enemy.telegraph
      const slam = enemy.slamTelegraph

      if (slam.active) {
        graphics.fillStyle(0xdc5162, 0.18)
        graphics.fillCircle(enemy.x, enemy.y, slam.radius)
        graphics.lineStyle(4, 0xff8b8b, 0.9)
        graphics.strokeCircle(enemy.x, enemy.y, slam.radius)
      }

      if (!telegraph.active) continue

      graphics.lineStyle(telegraph.width, 0xdc5162, 0.22)
      graphics.lineBetween(telegraph.start.x, telegraph.start.y, telegraph.end.x, telegraph.end.y)
      graphics.lineStyle(3, 0xff8b8b, 0.82)
      graphics.lineBetween(telegraph.start.x, telegraph.start.y, telegraph.end.x, telegraph.end.y)
    }
  }

  private drawHazards() {
    const graphics = this.hazardGraphics
    if (!graphics) return

    graphics.clear()
    for (const spot of this.state.fireSpots) {
      const alpha = Math.max(0.18, Math.min(0.48, spot.remaining / 20))
      graphics.fillStyle(0xff7a1a, alpha)
      graphics.fillCircle(spot.x, spot.y, spot.radius)
      graphics.lineStyle(2, 0xffd36d, 0.72)
      graphics.strokeCircle(spot.x, spot.y, spot.radius)
    }

    for (const fireball of this.state.fireballs) {
      graphics.fillStyle(0xffd36d, 1)
      graphics.fillCircle(fireball.x, fireball.y, fireball.radius + 4)
      graphics.fillStyle(0xdc5162, 1)
      graphics.fillCircle(fireball.x, fireball.y, fireball.radius)
      graphics.lineStyle(2, 0x090a0d, 1)
      graphics.strokeCircle(fireball.x, fireball.y, fireball.radius + 4)
    }

    for (const blade of this.state.spinningBlades) {
      graphics.fillStyle(0xd9f7ff, 0.95)
      graphics.fillCircle(blade.x, blade.y, blade.radius)
      graphics.lineStyle(5, 0x7ff5ff, 1)
      graphics.beginPath()
      for (let i = 0; i < 4; i += 1) {
        const angle = blade.rotation + (Math.PI / 2) * i
        graphics.moveTo(blade.x, blade.y)
        graphics.lineTo(blade.x + Math.cos(angle) * (blade.radius + 13), blade.y + Math.sin(angle) * (blade.radius + 13))
      }
      graphics.strokePath()
      graphics.lineStyle(2, 0x090a0d, 1)
      graphics.strokeCircle(blade.x, blade.y, blade.radius)
    }
  }

  private drawBoss() {
    const graphics = this.bossGraphics
    if (!graphics) return

    graphics.clear()
    for (const sprite of this.enemySprites.values()) sprite.setVisible(false)
    for (const enemy of getAllEnemies(this.state)) {
      if (enemy.hp <= 0) continue
      this.drawEnemyTargetRing(graphics, enemy)
      this.drawEnemy(graphics, enemy)
    }
  }

  private drawEnemy(graphics: Phaser.GameObjects.Graphics, enemy: EnemyState) {
    if (this.drawEnemySprite(enemy)) return

    if (enemy.kind === 'yian-kut-ku') {
      this.drawYianKutKu(graphics, enemy)
      return
    }

    if (enemy.kind === 'bird') {
      this.drawBird(graphics, enemy)
      return
    }

    if (enemy.kind === 'cyber-dragon') {
      this.drawCyberDragon(graphics, enemy)
      return
    }

    const isCharging = enemy.phase === 'charging'
    const isRecovering = enemy.phase === 'recovering'
    const isSlam = enemy.phase === 'slamWindup'
    const isBullfango = enemy.kind === 'bullfango'
    const bodyWidth = isBullfango ? enemy.radius * 2.25 : enemy.radius * 2.15
    const bodyHeight = isBullfango ? enemy.radius * 1.55 : enemy.radius * 1.65
    const hornScale = isBullfango ? 0.68 : 1

    graphics.fillStyle(isCharging ? 0xdc5162 : isSlam ? 0xe5b95f : isRecovering ? 0x8e68c4 : 0x7d4b38, 1)
    graphics.fillEllipse(enemy.x, enemy.y, bodyWidth, bodyHeight)
    graphics.fillStyle(0x3b211b, 1)
    graphics.fillTriangle(
      enemy.x - 20 * hornScale,
      enemy.y - 18 * hornScale,
      enemy.x - 44 * hornScale,
      enemy.y - 34 * hornScale,
      enemy.x - 28 * hornScale,
      enemy.y - 5 * hornScale,
    )
    graphics.fillTriangle(
      enemy.x + 20 * hornScale,
      enemy.y - 18 * hornScale,
      enemy.x + 44 * hornScale,
      enemy.y - 34 * hornScale,
      enemy.x + 28 * hornScale,
      enemy.y - 5 * hornScale,
    )
    graphics.fillStyle(0xf4eed8, 1)
    graphics.fillCircle(enemy.x - 12 * hornScale, enemy.y - 9 * hornScale, isBullfango ? 3 : 4)
    graphics.fillCircle(enemy.x + 12 * hornScale, enemy.y - 9 * hornScale, isBullfango ? 3 : 4)
    graphics.lineStyle(2, 0x090a0d, 1)
    graphics.strokeEllipse(enemy.x, enemy.y, bodyWidth, bodyHeight)
  }

  private drawEnemySprite(enemy: EnemyState) {
    const asset = ACTION_ENEMY_SPRITES[enemy.kind]
    const textureKey = `enemy-${enemy.kind}`
    if (!asset || !this.textures.exists(textureKey)) return false

    let sprite = this.enemySprites.get(enemy.id)
    if (!sprite) {
      sprite = this.add.image(enemy.x, enemy.y, textureKey)
      sprite.setDepth(8)
      this.enemySprites.set(enemy.id, sprite)
    }

    const size = enemy.radius * asset.displayScale
    sprite
      .setVisible(true)
      .setTexture(textureKey)
      .setPosition(enemy.x, enemy.y)
      .setDisplaySize(size, size)
      .setAlpha(enemy.phase === 'recovering' ? 0.85 : 1)
      .setAngle(enemy.kind === 'bird' && enemy.phase !== 'tracking' ? -8 : 0)

    return true
  }

  private drawYianKutKu(graphics: Phaser.GameObjects.Graphics, enemy: EnemyState) {
    const isCasting = enemy.phase === 'windup'
    graphics.fillStyle(isCasting ? 0xff9d3d : 0xd8772f, 1)
    graphics.fillEllipse(enemy.x, enemy.y, enemy.radius * 2.05, enemy.radius * 1.85)
    graphics.fillStyle(0xffd36d, 1)
    graphics.fillTriangle(enemy.x, enemy.y - 8, enemy.x + 45, enemy.y - 2, enemy.x, enemy.y + 10)
    graphics.fillStyle(0xb83b2f, 1)
    graphics.fillTriangle(enemy.x - 18, enemy.y - 8, enemy.x - 42, enemy.y - 25, enemy.x - 30, enemy.y + 7)
    graphics.fillTriangle(enemy.x + 10, enemy.y - 22, enemy.x + 22, enemy.y - 44, enemy.x + 31, enemy.y - 12)
    graphics.fillStyle(0xf4eed8, 1)
    graphics.fillCircle(enemy.x + 14, enemy.y - 9, 4)
    graphics.lineStyle(2, 0x090a0d, 1)
    graphics.strokeEllipse(enemy.x, enemy.y, enemy.radius * 2.05, enemy.radius * 1.85)
  }

  private drawBird(graphics: Phaser.GameObjects.Graphics, enemy: EnemyState) {
    const isFlying = enemy.phase === 'windup' || enemy.phase === 'charging' || enemy.phase === 'recovering'
    graphics.fillStyle(isFlying ? 0xf4eed8 : 0xb8e7ff, 1)
    graphics.fillEllipse(enemy.x, enemy.y, enemy.radius * 1.7, enemy.radius * 1.25)
    graphics.fillStyle(0x5d91ff, 1)
    graphics.fillTriangle(enemy.x - 8, enemy.y, enemy.x - 34, enemy.y - 12, enemy.x - 22, enemy.y + 10)
    graphics.fillTriangle(enemy.x + 8, enemy.y, enemy.x + 34, enemy.y - 12, enemy.x + 22, enemy.y + 10)
    graphics.fillStyle(0xffd36d, 1)
    graphics.fillTriangle(enemy.x + 13, enemy.y - 2, enemy.x + 28, enemy.y + 2, enemy.x + 13, enemy.y + 7)
    graphics.fillStyle(0x090a0d, 1)
    graphics.fillCircle(enemy.x + 7, enemy.y - 5, 3)
    graphics.lineStyle(2, 0x090a0d, 1)
    graphics.strokeEllipse(enemy.x, enemy.y, enemy.radius * 1.7, enemy.radius * 1.25)
  }

  private drawCyberDragon(graphics: Phaser.GameObjects.Graphics, enemy: EnemyState) {
    const isCharging = enemy.phase === 'charging' || enemy.phase === 'circling'
    graphics.fillStyle(isCharging ? 0x89f7ff : 0x31566f, 1)
    graphics.fillEllipse(enemy.x, enemy.y, enemy.radius * 2.3, enemy.radius * 1.55)
    graphics.fillStyle(0x4aa3c7, 1)
    graphics.fillTriangle(enemy.x + 18, enemy.y - 4, enemy.x + 54, enemy.y - 18, enemy.x + 45, enemy.y + 15)
    graphics.fillTriangle(enemy.x - 16, enemy.y - 16, enemy.x - 42, enemy.y - 48, enemy.x - 4, enemy.y - 28)
    graphics.fillTriangle(enemy.x - 18, enemy.y + 15, enemy.x - 46, enemy.y + 48, enemy.x - 1, enemy.y + 30)
    graphics.fillStyle(0xff4f7d, 1)
    graphics.fillCircle(enemy.x + 18, enemy.y - 7, 5)
    graphics.lineStyle(3, 0xd9f7ff, 0.9)
    graphics.lineBetween(enemy.x - 22, enemy.y - 8, enemy.x + 8, enemy.y - 8)
    graphics.lineBetween(enemy.x - 24, enemy.y + 8, enemy.x + 10, enemy.y + 8)
    graphics.lineStyle(2, 0x090a0d, 1)
    graphics.strokeEllipse(enemy.x, enemy.y, enemy.radius * 2.3, enemy.radius * 1.55)
  }

  private drawPlayer() {
    const graphics = this.playerGraphics
    if (!graphics) return

    const player = this.state.player
    graphics.clear()
    this.drawTargetRing(graphics, player)
    this.drawUnitIcon(graphics, player, player.stunTimer > 0 ? 0xe5b95f : 0x30ff7a)

    if (player.shield > 0) {
      graphics.lineStyle(2, 0xb8e7ff, 0.85)
      graphics.strokeCircle(player.x, player.y, player.radius + 9)
    }

    if (player.stunTimer > 0) {
      graphics.fillStyle(0xe5b95f, 1)
      graphics.fillCircle(player.x - 10, player.y - 24, 3)
      graphics.fillCircle(player.x + 2, player.y - 29, 3)
      graphics.fillCircle(player.x + 13, player.y - 23, 3)
    }

    if (player.bleedTimer > 0) {
      graphics.lineStyle(3, 0xff6b84, 0.9)
      graphics.strokeCircle(player.x, player.y, player.radius + 13)
    }
  }

  private drawParty() {
    const graphics = this.partyGraphics
    if (!graphics) return

    graphics.clear()
    for (const member of this.state.party) {
      this.drawTargetRing(graphics, member)
      this.drawUnitIcon(graphics, member, this.getUnitColor(member), member.hp > 0 ? 1 : 0.38)

      if (member.renewTimer > 0) {
        graphics.lineStyle(2, 0x3f9a66, 0.9)
        graphics.strokeCircle(member.x, member.y, member.radius + 5)
      }

      if (member.shield > 0) {
        graphics.lineStyle(2, 0xb8e7ff, 0.85)
        graphics.strokeCircle(member.x, member.y, member.radius + 9)
      }

      if (member.stunTimer > 0) {
        graphics.fillStyle(0xe5b95f, 1)
        graphics.fillCircle(member.x - 8, member.y - 22, 3)
        graphics.fillCircle(member.x + 6, member.y - 24, 3)
      }

      if (member.bleedTimer > 0) {
        graphics.lineStyle(3, 0xff6b84, 0.9)
        graphics.strokeCircle(member.x, member.y, member.radius + 13)
      }
    }
  }

  private drawTargetRing(graphics: Phaser.GameObjects.Graphics, unit: TargetableState) {
    if (this.state.targetId !== unit.id) return
    graphics.lineStyle(3, 0xe5b95f, 1)
    graphics.strokeCircle(unit.x, unit.y, unit.radius + 9)
  }

  private drawEnemyTargetRing(graphics: Phaser.GameObjects.Graphics, enemy: EnemyState) {
    if (this.state.targetId !== enemy.id) return
    graphics.lineStyle(4, 0xe55353, 1)
    graphics.strokeCircle(enemy.x, enemy.y, enemy.radius + 12)
  }

  private drawUnitIcon(
    graphics: Phaser.GameObjects.Graphics,
    unit: TargetableState,
    color: number,
    alpha = 1,
  ) {
    graphics.fillStyle(color, alpha)
    graphics.fillCircle(unit.x, unit.y, unit.radius)
    graphics.lineStyle(2, unit.invulnerableTimer > 0 ? 0xffffff : 0x090a0d, alpha)
    graphics.strokeCircle(unit.x, unit.y, unit.radius)

    if (unit.role === 'healer') {
      graphics.lineStyle(3, 0x082815, alpha)
      graphics.lineBetween(unit.x - 7, unit.y, unit.x + 7, unit.y)
      graphics.lineBetween(unit.x, unit.y - 7, unit.x, unit.y + 7)
      return
    }

    if (unit.role === 'tank') {
      graphics.fillStyle(0x3b2d12, alpha)
      graphics.fillTriangle(unit.x, unit.y - 10, unit.x - 9, unit.y - 4, unit.x + 9, unit.y - 4)
      graphics.fillTriangle(unit.x - 9, unit.y - 4, unit.x + 9, unit.y - 4, unit.x, unit.y + 10)
      graphics.lineStyle(2, 0xfff0a8, alpha)
      graphics.strokeTriangle(unit.x, unit.y - 10, unit.x - 9, unit.y - 4, unit.x + 9, unit.y - 4)
      graphics.lineBetween(unit.x - 9, unit.y - 4, unit.x, unit.y + 10)
      graphics.lineBetween(unit.x + 9, unit.y - 4, unit.x, unit.y + 10)
      return
    }

    if (unit.role === 'melee') {
      graphics.lineStyle(3, 0x16090b, alpha)
      graphics.lineBetween(unit.x - 7, unit.y + 7, unit.x + 7, unit.y - 7)
      graphics.lineBetween(unit.x - 4, unit.y - 7, unit.x + 7, unit.y + 4)
      return
    }

    graphics.lineStyle(3, 0x10091c, alpha)
    graphics.strokeCircle(unit.x, unit.y, 7)
    graphics.lineBetween(unit.x - 9, unit.y, unit.x + 9, unit.y)
  }

  private getUnitColor(unit: TargetableState) {
    if (unit.role === 'tank') return 0xe5b95f
    if (unit.id === 'melee-1') return 0xdc5162
    if (unit.id === 'melee-2') return 0xf08a4b
    if (unit.id === 'ranged-1') return 0x8e68c4
    if (unit.id === 'ranged-2') return 0x5d91ff
    return 0x30ff7a
  }

  private drawFx() {
    const graphics = this.fxGraphics
    if (!graphics) return

    graphics.clear()
    if (this.state.lastHit === 'boss') {
      graphics.lineStyle(4, 0xb8e7ff, 0.85)
      for (const enemy of getAllEnemies(this.state)) {
        if (enemy.hp > 0) graphics.strokeCircle(enemy.x, enemy.y, enemy.radius + 12)
      }
    }
    if (this.state.lastHit === 'player') {
      graphics.lineStyle(5, 0xdc5162, 0.8)
      graphics.strokeCircle(this.state.player.x, this.state.player.y, this.state.player.radius + 16)
    }
  }
}
