import * as WeatherData from '../../data/weatherData'

import { getRandomInt, Vector } from '../../utils'
import { world, system, Player, EntityDamageCause } from '@minecraft/server'

import { getBiome } from '../biome'

const minecraftGlasses = [
    "minecraft:glass",
    "minecraft:orange_stained_glass",
    "minecraft:black_stained_glass",
    "minecraft:green_stained_glass",
    "minecraft:blue_stained_glass",
    "minecraft:gray_stained_glass",
    "minecraft:light_gray_stained_glass",
    "minecraft:lime_stained_glass",
    "minecraft:red_stained_glass",
    "minecraft:pink_stained_glass",
    "minecraft:magenta_stained_glass",
    "minecraft:cyan_stained_glass",
    "minecraft:purple_stained_glass",
    "minecraft:light_blue_stained_glass",
    "minecraft:white_stained_glass",
    "minecraft:brown_stained_glass",
    "minecraft:yellow_stained_glass",
    "minecraft:orange_stained_glass"
]
system.runInterval(() => {
    world.getDimension('overworld').getPlayers({ farthest: 1 }).find(player => player.runCommandAsync(`scriptevent minty_ew:pick_random`))
}, 20 * 60 * 24) // every 5 seconds

system.afterEvents.scriptEventReceive.subscribe(async (event) => {
    if (!event.id.startsWith('minty_ew:')) return

    if (event.id.includes('pick_random')) {
        const biome = await getBiome(world.getAllPlayers()[0])

        if (!biome || biome === undefined) return

        let weatherBiome = WeatherData.biomeWeathers.find(weatherBiome => weatherBiome.identifier === biome)
        if (!weatherBiome || weatherBiome === undefined) weatherBiome = WeatherData.biomeWeathers[0]

        // console.warn(weatherBiome.identifier)

        const randomDisaster = weatherBiome.events[Math.floor(Math.random() * weatherBiome.events.length)]
        event.sourceEntity.runCommand(`scriptevent minty_ew:${randomDisaster}`)
    }

    if (event.id.includes('unbug')) world.setDynamicProperty('minty_ew:disaster_already_started', false)

    const weatherType = WeatherData.weatherTypes.find(weather => weather.identifier === event.id.split(':')[1])
    if (!weatherType || weatherType === undefined) return

    const player = event.sourceEntity

    //const identifier = weatherType.identifier.charAt(0).toUpperCase() + weatherType.identifier.slice(1)
    const identifier = weatherType.identifier.replace(/_/g, ' ').replace(/\b\w/g, char => char.toUpperCase())
    const duration = getRandomInt(weatherType.duration.min, weatherType.duration.max)

    let showDuration = duration

    if (world.getDynamicProperty('minty_ew:disaster_already_started')) {
        player.dimension.runCommandAsync(`tellraw "${player.nameTag}" {"rawtext":[{"text":"§cA disaster is already underway."}]}`)
        return
    }

    let weatherRuntimer = system.runInterval(() => {

        // Screen functions
        showDuration = showDuration - 1

        if (weatherType.particles && weatherType.particles.length >= 1 && !weatherType.customMob) {
            weatherType.particles.forEach(particle => {
                const particleOffset = {
                    x: player.location.x + particle.offset.x,
                    y: (weatherType.identifier.match(/^(cyclone|huricane|thunderstorm)$/) ? 120 + particle.offset.y : player.location.y + particle.offset.y),
                    z: player.location.z + particle.offset.z
                }

                player.dimension.spawnParticle(particle.name, particleOffset)
            })
        }

        // Camera functions
        if (weatherType.camera.shake)
            player.dimension.runCommand(
                `camerashake add "${player.nameTag}" ${weatherType.camera.shakeIntensity} ${duration} positional`
            )

        // Daylight checker
        if (weatherType.daylight && weatherType.daylight.detectDaylight) {
            const maxHeightBlocks = 319 - player.location.y
            let validBlocks = 0

            for (let y = player.location.y + 1; y <= player.dimension.heightRange.max - 1; y++) {
                if (validBlocks < maxHeightBlocks) {
                    const position = Vector(player.location.x, y, player.location.z)
                    const block = player.dimension.getBlock(position)

                    if (block.isAir && block) validBlocks += 1
                    else if (block) if (minecraftGlasses.includes(block.typeId)) validBlocks += 1
                }

                if (validBlocks == maxHeightBlocks) {
                    player.setDynamicProperty('minty_ew:is_on_daylight', true)
                } else {
                    player.setDynamicProperty('minty_ew:is_on_daylight', false)
                }
            }

            if (player.getDynamicProperty('minty_ew:is_on_daylight')) {
                if (weatherType.daylight.dealsDamage) {
                    player.applyDamage(weatherType.daylight.damage, {
                        cause: EntityDamageCause.lava
                    })
                }
            }
        }

        world.setDynamicProperty('minty_ew:disaster_already_started', true)
    }, 20)

    if (weatherType.entityRain && weatherType.entityRain.enabled) {
        let rainEntityId = weatherType.entityRain.identifier

        const customEntityRainRuntimer = system.runInterval(() => {
            for (let amount = 1; amount < weatherType.entityRain.entityAmount; amount++) {
                if (rainEntityId !== 'pet_rain') {
                    const rainEntity = player.dimension.spawnEntity(rainEntityId, {
                        x: player.location.x + Math.floor(Math.random() * weatherType.entityRain.radius),
                        y: player.location.y + weatherType.entityRain.offsetY,
                        z: player.location.z + Math.floor(Math.random() * weatherType.entityRain.radius)
                    })

                    rainEntity.addTag('minty_ew:entity_rain_remove')
                } else {
                    const dogRain = player.dimension.spawnEntity('minecraft:wolf', {
                        x: player.location.x + Math.floor(Math.random() * weatherType.entityRain.radius),
                        y: player.location.y + weatherType.entityRain.offsetY,
                        z: player.location.z + Math.floor(Math.random() * weatherType.entityRain.radius)
                    })

                    const catRain = player.dimension.spawnEntity('minecraft:cat', {
                        x: player.location.x + Math.floor(Math.random() * weatherType.entityRain.radius),
                        y: player.location.y + weatherType.entityRain.offsetY,
                        z: player.location.z + Math.floor(Math.random() * weatherType.entityRain.radius)
                    })

                    dogRain.addTag('minty_ew:entity_rain_remove')
                    dogRain.addEffect('slow_falling', 6, { amplifier: 1 })
                    catRain.addTag('minty_ew:entity_rain_remove')
                    catRain.addEffect('slow_falling', 6, { amplifier: 1 })
                }
            }
        }, 20 * weatherType.entityRain.timer)

        system.runInterval(() => {
            system.clearRun(customEntityRainRuntimer)
            player.dimension.getEntities({
                tags: ['minty_ew:entity_rain_remove']
            }).forEach(entity => entity.remove())
        }, 20 * duration)
    }

    if (weatherType.customMob) {
        let customEntity
        const spawnLocation = {
            x: player.location.x + Math.floor(Math.random() * 16),
            y: player.location.y,
            z: player.location.z + Math.floor(Math.random() * 16)
        }

        customEntity = player.dimension.spawnEntity('minty_ew:' + weatherType.customMobType, spawnLocation)
        const customMobRuntimer = system.runInterval(() => {
            if (weatherType.velocity.pullUpwards)
                customEntity.runCommandAsync(`effect @e[type=!${customEntity.typeId},r=${weatherType.velocity.pullRadius}] levitation 2 1`)

            if (weatherType.velocity.pullsToCenter)
                customEntity.runCommandAsync(`execute as @e[type=!${customEntity.typeId},r=${weatherType.velocity.pullRadius},type=!player] run tp ^ ^ ^0.3 facing @s`)

            if (weatherType.velocity.spreadEntities && weatherType.velocity.pullRadius >= 11)
                customEntity.runCommandAsync(`spreadplayers ~ ~ 0.1 ${weatherType.velocity.pullRadius} @e[type=!${customEntity.typeId},r=${weatherType.velocity.pullRadius},type=!player]`)
        })
        const customMobParticleRuntimer = system.runInterval(() => {
            if (weatherType.particles && weatherType.particles.length >= 1) {
                weatherType.particles.forEach(particle => {
                    try {
                        customEntity.dimension.spawnParticle(particle.name, {
                            x: customEntity.location.x + particle.offset.x,
                            y: customEntity.location.y + particle.offset.y,
                            z: customEntity.location.z + particle.offset.z
                        })
                    } catch { }
                })
            }
        }, 20 * 3)
        system.runTimeout(() => {
            customEntity.triggerEvent('minty_ew:despawn')
            system.clearRun(customMobRuntimer)
            system.clearRun(customMobParticleRuntimer)
        }, 20 * duration)
    }

    if (weatherType.normalRain)
        player.dimension.runCommand(`execute at "${player.nameTag}" run weather thunder`)

    system.runTimeout(() => {
        system.clearRun(weatherRuntimer)
        player.dimension.runCommandAsync(`camerashake stop "${player.nameTag}"`)
        world.setDynamicProperty('minty_ew:disaster_already_started', false)
        if (weatherType.normalRain) player.dimension.runCommandAsync(`execute at "${player.nameTag}" run weather clear`)
    }, 20 * duration)
})

world.afterEvents.entityHurt.subscribe(event => {
    if (!event.hurtEntity.hasComponent('minecraft:is_charged')) return
    if (event.damageSource.cause != EntityDamageCause.suffocation) return

    const maxHeightBlocks = 319 - event.hurtEntity.location.y

    for (let y = event.hurtEntity.location.y + 1; y <= event.hurtEntity.dimension.heightRange.max - 1; y++) {
        const position = Vector(event.hurtEntity.location.x, y, event.hurtEntity.location.z)
        const block = event.hurtEntity.dimension.getBlock(position)

        if (block.isAir && block.location.y < maxHeightBlocks)
            event.hurtEntity.tryTeleport(block.location, { checkForBlocks: true })
    }
})

world.afterEvents.playerSpawn.subscribe(event => {
    event.player.runCommand(`execute as @s[tag=!minty_ew_book] run give @s minty_ew:guidebook`)
    event.player.runCommand(`execute as @s[tag=!minty_ew_book] run tag @s add minty_ew_book`)
})