import config from '@/application/config'
import { Direction } from '@/application/enums'
import { type MapCharacter } from '@/application/types'
import { calculateIsometricDepth, tileToWorldX, tileToWorldY } from '@/services/mapService'
import { loadSpriteTextures } from '@/services/textureService'
import { CharacterTypeStorage } from '@/storage/storages'
import { refObj } from 'phavuer'
import { computed, ref } from 'vue'

export function useCharacterSpriteComposable(scene: Phaser.Scene, tilemap: Phaser.Tilemaps.Tilemap, mapCharacter: MapCharacter) {
  const characterContainer = refObj<Phaser.GameObjects.Container>()
  const characterSpriteId = ref('')
  const characterSprite = refObj<Phaser.GameObjects.Sprite>()
  const currentPositionX = ref(0)
  const currentPositionY = ref(0)
  const isometricDepth = ref(1)
  const isInitialPosition = ref(true)
  const tween = ref<Phaser.Tweens.Tween | null>(null)

  const updateIsometricDepth = (positionX: number, positionY: number) => {
    isometricDepth.value = calculateIsometricDepth(positionX, positionY, 30, 95, true)
  }

  const updatePosition = (positionX: number, positionY: number) => {
    const newPositionX = tileToWorldX(tilemap, positionX, positionY)
    const newPositionY = tileToWorldY(tilemap, positionX, positionY)

    if (isInitialPosition.value) {
      currentPositionX.value = newPositionX
      currentPositionY.value = newPositionY
      isInitialPosition.value = false
      return
    }

    if (tween.value?.isPlaying()) {
      tween.value.stop()
    }

    const tileDistance = Math.sqrt(Math.pow((newPositionX - currentPositionX.value) / config.tile_size.width, 2) + Math.pow((newPositionY - currentPositionY.value) / config.tile_size.height, 2))

    const baseDuration = 300 // milliseconds per tile
    const duration = Math.min(baseDuration * tileDistance, baseDuration)

    tween.value = tilemap.scene.tweens.add({
      targets: characterContainer.value,
      x: newPositionX,
      y: newPositionY,
      duration,
      ease: 'Linear',
      onStart: () => {
        updateIsometricDepth(positionX, positionY)
      },
      onUpdate: () => {
        updateIsometricDepth(positionX, positionY)
        currentPositionX.value = characterContainer.value?.x ?? currentPositionX.value
        currentPositionY.value = characterContainer.value?.y ?? currentPositionY.value
      },
      onComplete: () => {
        updateIsometricDepth(positionX, positionY)
      }
    })
  }

  const playAnimation = (animation: string, loop = false, ignoreIfPlaying = true) => {
    if (!characterSprite.value || !characterSpriteId.value) return

    const fullAnimationName = `${characterSpriteId.value}-${animation}_${currentDirection.value}`

    // Remove any existing animation complete listeners
    characterSprite.value.off(Phaser.Animations.Events.ANIMATION_COMPLETE)

    // Add new listener
    characterSprite.value.on(Phaser.Animations.Events.ANIMATION_COMPLETE, () => {
      characterSprite.value!.setFrame(0)
      characterSprite.value!.setTexture(charTexture.value)
    })

    characterSprite.value.anims.play(
      {
        key: fullAnimationName,
        repeat: loop ? -1 : 0
      },
      ignoreIfPlaying
    )
  }

  const calcDirection = (oldPositionX: number, oldPositionY: number, newPositionX: number, newPositionY: number): Direction => {
    if (newPositionY < oldPositionY || newPositionX < oldPositionX) return Direction.NEGATIVE
    if (newPositionX > oldPositionX || newPositionY > oldPositionY) return Direction.POSITIVE
    return Direction.UNCHANGED
  }

  const isFlippedX = computed(() => [6, 4].includes(mapCharacter.character.rotation ?? 0))

  const currentDirection = computed(() => {
    return [0, 6].includes(mapCharacter.character.rotation ?? 0) ? 'left_up' : 'right_down'
  })

  const currentAction = computed(() => {
    return mapCharacter.isMoving ? 'walk' : 'idle'
  })

  const charTexture = computed(() => {
    const spriteId = characterSpriteId.value ?? 'idle_right_down'
    return `${spriteId}-${currentAction.value}_${currentDirection.value}`
  })

  const updateSprite = () => {
    if (!characterSprite.value) return

    if (mapCharacter.isMoving) {
      characterSprite.value.anims.play(charTexture.value, true)
    } else {
      characterSprite.value.anims.stop()
      characterSprite.value.setFrame(0)
      characterSprite.value.setTexture(charTexture.value)
    }
  }

  const initializeSprite = async () => {
    const characterTypeStorage = new CharacterTypeStorage()
    const spriteId = await characterTypeStorage.getSpriteId(mapCharacter.character.characterType!)
    if (!spriteId) return

    characterSpriteId.value = spriteId
    await loadSpriteTextures(scene, spriteId)

    if (characterContainer.value) {
      characterContainer.value.setName(mapCharacter.character.name)
    }

    if (characterSprite.value) {
      characterSprite.value.setTexture(charTexture.value)
      characterSprite.value.setFlipX(isFlippedX.value)
    }

    updatePosition(mapCharacter.character.positionX, mapCharacter.character.positionY)
    updateIsometricDepth(mapCharacter.character.positionX, mapCharacter.character.positionY)
  }

  const cleanup = () => {
    tween.value?.stop()
  }

  return {
    characterContainer,
    characterSpriteId,
    characterSprite,
    currentPositionX,
    currentPositionY,
    isometricDepth,
    isFlippedX,
    updatePosition,
    playAnimation,
    calcDirection,
    updateSprite,
    initializeSprite,
    cleanup
  }
}
