<template>
  <Scene name="effects" @preload="preloadScene" @create="createScene" @update="updateScene" />
</template>

<script setup lang="ts">
import type { Map, WeatherState } from '@/application/types'
import { MapStorage } from '@/storage/storages'
import { useGameStore } from '@/stores/gameStore'
import { useMapStore } from '@/stores/mapStore'
import { Scene } from 'phavuer'
import { onBeforeUnmount, onMounted, ref, watch } from 'vue'

// Constants
const LIGHT_CONFIG = {
  SUNRISE_HOUR: 6,
  SUNSET_HOUR: 20,
  DAY_STRENGTH: 100,
  NIGHT_STRENGTH: 30
}

// Stores and refs
const gameStore = useGameStore()
const mapStore = useMapStore()
const mapStorage = new MapStorage()
const sceneRef = ref<Phaser.Scene | null>(null)
const mapEffectsReady = ref(false)
const mapObject = ref<Map | null>(null)

// Effect objects
const effects = {
  light: ref<Phaser.GameObjects.Graphics | null>(null),
  rain: ref<Phaser.GameObjects.Particles.ParticleEmitter | null>(null),
  fog: ref<Phaser.GameObjects.Sprite | null>(null)
}

// Weather state
const weatherState = ref<WeatherState>({
  isRainEnabled: false,
  rainPercentage: 0,
  isFogEnabled: false,
  fogDensity: 0
})

// Scene setup
const preloadScene = (scene: Phaser.Scene) => {
  scene.load.image('raindrop', 'assets/raindrop.png')
  scene.load.image('fog', 'assets/fog.png')
}

const createScene = (scene: Phaser.Scene) => {
  sceneRef.value = scene
  initializeEffects(scene)
  setupSocketListeners()
}

const loadMap = async () => {
  if (!mapStore.mapId) return
  mapObject.value = await mapStorage.get(mapStore.mapId)
}

// Watch for mapId changes and load map when it's available
watch(
  () => mapStore.mapId,
  async (newMapId) => {
    if (newMapId) {
      mapEffectsReady.value = false
      await loadMap()
      updateScene()
    }
  },
  { immediate: true }
)

const initializeEffects = (scene: Phaser.Scene) => {
  // Light
  effects.light.value = scene.add.graphics().setDepth(1000)

  // Rain
  effects.rain.value = scene.add
    .particles(0, 0, 'raindrop', {
      x: { min: 0, max: window.innerWidth },
      y: -50,
      quantity: 5,
      lifespan: 2000,
      speedY: { min: 300, max: 500 },
      scale: { start: 0.005, end: 0.005 },
      alpha: { start: 0.5, end: 0 },
      blendMode: 'ADD'
    })
    .setDepth(900)
  effects.rain.value.stop()

  // Fog
  effects.fog.value = scene.add
    .sprite(window.innerWidth / 2, window.innerHeight / 2, 'fog')
    .setScale(2)
    .setAlpha(0)
    .setDepth(950)
}

// Effect updates
const updateScene = () => {
  const timeBasedLight = calculateLightStrength(gameStore.world.date)
  const mapEffects = mapObject.value?.mapEffects?.reduce(
    (acc, curr) => ({
      ...acc,
      [curr.effect]: curr.strength
    }),
    {}
  ) as { [key: string]: number }

  // Only update effects once mapEffects are loaded
  if (!mapEffectsReady.value) {
    if (mapObject.value) {
      mapEffectsReady.value = true
    } else {
      return
    }
  }

  const finalEffects =
    mapEffects && Object.keys(mapEffects).length
      ? mapEffects
      : {
          light: timeBasedLight,
          rain: weatherState.value.isRainEnabled ? weatherState.value.rainPercentage : 0,
          fog: weatherState.value.isFogEnabled ? weatherState.value.fogDensity * 100 : 0
        }
  applyEffects(finalEffects)
}

const applyEffects = (effectValues: any) => {
  if (effects.light.value) {
    const darkness = 1 - (effectValues.light ?? 100) / 100
    effects.light.value.clear().fillStyle(0x000000, darkness).fillRect(0, 0, window.innerWidth, window.innerHeight)
  }

  if (effects.rain.value) {
    const strength = effectValues.rain ?? 0
    strength > 0 ? effects.rain.value.start().setQuantity(Math.floor((strength / 100) * 10)) : effects.rain.value.stop()
  }

  if (effects.fog.value) {
    effects.fog.value.setAlpha((effectValues.fog ?? 0) / 100)
  }
}

const calculateLightStrength = (time: Date): number => {
  const hour = time.getHours()
  const minute = time.getMinutes()

  if (hour >= LIGHT_CONFIG.SUNSET_HOUR || hour < LIGHT_CONFIG.SUNRISE_HOUR) return LIGHT_CONFIG.NIGHT_STRENGTH

  if (hour > LIGHT_CONFIG.SUNRISE_HOUR && hour < LIGHT_CONFIG.SUNSET_HOUR - 2) return LIGHT_CONFIG.DAY_STRENGTH

  if (hour === LIGHT_CONFIG.SUNRISE_HOUR) return LIGHT_CONFIG.NIGHT_STRENGTH + ((LIGHT_CONFIG.DAY_STRENGTH - LIGHT_CONFIG.NIGHT_STRENGTH) * minute) / 60

  const totalMinutes = (hour - (LIGHT_CONFIG.SUNSET_HOUR - 2)) * 60 + minute
  return LIGHT_CONFIG.DAY_STRENGTH - (LIGHT_CONFIG.DAY_STRENGTH - LIGHT_CONFIG.NIGHT_STRENGTH) * (totalMinutes / 120)
}

// Socket and window handlers
const setupSocketListeners = () => {
  gameStore.connection?.emit('weather', (response: WeatherState) => {
    weatherState.value = response
    updateScene()
  })

  gameStore.connection?.on('weather', (data: WeatherState) => {
    weatherState.value = data
    updateScene()
  })

  gameStore.connection?.on('date', updateScene)
}

const handleResize = () => {
  if (effects.rain.value) effects.rain.value.updateConfig({ x: { min: 0, max: window.innerWidth } })
  if (effects.fog.value) effects.fog.value.setPosition(window.innerWidth / 2, window.innerHeight / 2)
}

// Lifecycle
watch(
  () => mapObject.value,
  () => {
    mapEffectsReady.value = false
    updateScene()
  },
  { deep: true }
)

onMounted(() => window.addEventListener('resize', handleResize))

onBeforeUnmount(() => {
  window.removeEventListener('resize', handleResize)
  if (sceneRef.value) sceneRef.value.scene.remove('effects')
  gameStore.connection?.off('weather')
})
</script>
