use crate::ecs::EditorWorld;
use nightshade::prelude::*;

pub fn spawn_with_shadows(editor_world: &mut EditorWorld, world: &mut World) {
    let sun = spawn_sun(world);
    if let Some(light) = world.core.get_light_mut(sun) {
        light.cast_shadows = true;
        light.intensity = 3.5;
        light.shadow_bias = 0.008;
    }
    editor_world.resources.sun.sun_entity = Some(sun);
    editor_world
        .resources
        .editor_scene
        .register_scaffolding(sun);
    update(editor_world, world);
}

pub fn despawn_default(editor_world: &mut EditorWorld, world: &mut World) {
    let Some(sun) = editor_world.resources.sun.sun_entity.take() else {
        return;
    };
    editor_world
        .resources
        .editor_scene
        .unregister_scaffolding(sun);
    despawn_recursive_immediate(world, sun);
}

pub fn scene_has_lights(editor_world: &EditorWorld, world: &World) -> bool {
    use nightshade::ecs::world::LIGHT;
    let mut has = false;
    world.core.query().with(LIGHT).iter(|entity, _, _| {
        if !editor_world.resources.editor_scene.is_scaffolding(entity) {
            has = true;
        }
    });
    has
}

pub fn find_scene_directional_light(editor_world: &EditorWorld, world: &World) -> Option<Entity> {
    use nightshade::ecs::light::components::LightType;
    use nightshade::ecs::world::LIGHT;
    let mut result = None;
    world.core.query().with(LIGHT).iter(|entity, _, _| {
        if result.is_some() {
            return;
        }
        if editor_world.resources.editor_scene.is_scaffolding(entity) {
            return;
        }
        if let Some(light) = world.core.get_light(entity)
            && matches!(light.light_type, LightType::Directional)
        {
            result = Some(entity);
        }
    });
    result
}

pub fn adopt_scene_sun(editor_world: &mut EditorWorld, world: &mut World) {
    if let Some(sun) = find_scene_directional_light(editor_world, world) {
        editor_world.resources.sun.sun_entity = Some(sun);
        update(editor_world, world);
    }
}

pub fn update(editor_world: &mut EditorWorld, world: &mut World) {
    if !editor_world.resources.sun.auto_cycle {
        return;
    }
    let Some(sun) = editor_world.resources.sun.sun_entity else {
        return;
    };
    let hour = editor_world.resources.sun.day_night_hour;
    let sun_dir = direction_for_hour(hour);
    let is_night = !(6.0..=18.0).contains(&hour);

    let sun_intensity = if is_night {
        0.0
    } else {
        let elevation = sun_dir.y.max(0.0);
        3.5 * elevation.sqrt()
    };

    let warm = Vec3::new(1.0, 0.7, 0.4);
    let white = Vec3::new(1.0, 0.95, 0.8);
    let sun_color = if is_night {
        Vec3::new(0.0, 0.0, 0.0)
    } else if hour < 7.5 {
        let t = ((hour - 6.0) / 1.5).clamp(0.0, 1.0);
        nalgebra_glm::lerp(&warm, &white, t)
    } else if hour > 16.5 {
        let t = ((18.0 - hour) / 1.5).clamp(0.0, 1.0);
        nalgebra_glm::lerp(&warm, &white, t)
    } else {
        white
    };

    if let Some(light) = world.core.get_light_mut(sun) {
        light.intensity = sun_intensity;
        light.color = sun_color;
    }

    let sun_position = sun_dir * 100.0;
    if let Some(transform) = world.core.get_local_transform_mut(sun) {
        transform.translation = sun_position;
        let forward = -sun_dir;
        let up = Vec3::y();
        let right = nalgebra_glm::normalize(&nalgebra_glm::cross(&forward, &up));
        if right.norm() > 0.001 {
            let corrected_up = nalgebra_glm::normalize(&nalgebra_glm::cross(&right, &forward));
            transform.rotation = nalgebra_glm::mat3_to_quat(&nalgebra_glm::Mat3::from_columns(&[
                right,
                corrected_up,
                -forward,
            ]));
        }
    }
    mark_local_transform_dirty(world, sun);
}

pub fn direction_for_hour(hour: f32) -> Vec3 {
    let pi = std::f32::consts::PI;
    if !(6.0..=18.0).contains(&hour) {
        Vec3::new(0.0, -1.0, 0.0)
    } else {
        let sun_angle = (hour - 6.0) / 12.0 * pi;
        nalgebra_glm::normalize(&Vec3::new(-sun_angle.cos(), sun_angle.sin(), -0.3))
    }
}
