use godot::{
    classes::{
        AnimatedSprite2D, Area2D, CollisionShape2D, InputEvent, InputEventMouseButton, Label,
        RectangleShape2D,
    },
    global::MouseButton,
    prelude::*,
};

use crate::{consts, core::event_bus::EventBus, data::hazard_def::HazardDef};

#[derive(GodotClass)]
#[class(base=Node2D)]
pub struct HazardController {
    #[export]
    anim_sprite: Option<Gd<AnimatedSprite2D>>,
    #[export]
    click_area: Option<Gd<Area2D>>,
    #[export]
    click_shape: Option<Gd<CollisionShape2D>>,
    #[export]
    name_label: Option<Gd<Label>>,

    data: Option<Gd<HazardDef>>,
    time_active: f32,
    is_resolved: bool,
    current_health: i32,
    event_bus: Option<Gd<EventBus>>,

    base: Base<Node2D>,
}

#[godot_api]
impl INode2D for HazardController {
    fn init(base: Base<Node2D>) -> Self {
        Self {
            anim_sprite: None,
            click_area: None,
            click_shape: None,
            name_label: None,
            data: None,
            time_active: 0.0,
            is_resolved: false,
            current_health: 1,
            event_bus: None,
            base,
        }
    }

    fn ready(&mut self) {
        let bus = self.base().get_node_as::<EventBus>(consts::EVENT_BUS_PATH);

        self.event_bus = Some(bus);

        if let Some(mut area) = self.click_area.clone() {
            let callable = self.base_mut().callable("on_input_event");
            area.connect("input_event", &callable);
        }
    }

    fn process(&mut self, delta: f64) {
        if self.is_resolved {
            return;
        }

        let Some(data) = &self.data.clone() else {
            return;
        };
        let data_bind = data.bind();

        self.time_active += delta as f32;

        if self.time_active >= data_bind.time_to_fail {
            if let Some(bus) = &self.event_bus {
                bus.clone().bind_mut().publish_lift_completed(false);
            }

            self.base_mut().queue_free();
        }
    }
}

#[godot_api]
impl HazardController {
    #[func]
    pub fn initialize(&mut self, data: Gd<HazardDef>) {
        let data_bind = data.bind();
        self.current_health = data_bind.clicks_to_resolve;

        if let Some(sprite) = &mut self.anim_sprite {
            if let Some(frames) = &data_bind.animations {
                sprite.set_sprite_frames(frames);

                let anim_name = StringName::from(&data_bind.idle_anim_name);

                if let Some(texture) = frames.get_frame_texture(&anim_name, 0) {
                    if let Some(shape) = &mut self.click_shape {
                        let mut rect = RectangleShape2D::new_gd();
                        rect.set_size(texture.get_size());
                        shape.set_shape(&rect.upcast::<RectangleShape2D>());
                    }
                }

                sprite.play_ex().name(&anim_name).done();
            }
        }

        if let Some(label) = &mut self.name_label {
            label.set_text(&data_bind.display_name);
        }

        self.base_mut().set_scale(Vector2::ZERO);

        let node = self.base().clone();

        if let Some(mut tween) = self.base_mut().create_tween() {
            tween
                .set_trans(godot::classes::tween::TransitionType::BACK)
                .unwrap()
                .set_ease(godot::classes::tween::EaseType::OUT);

            tween.tween_property(
                &node.upcast::<Object>(),
                "scale",
                &Vector2::ONE.to_variant(),
                0.4,
            );
        }

        drop(data_bind);
        self.data = Some(data);
    }

    #[func]
    fn on_input_event(&mut self, _viewport: Gd<Node>, event: Gd<InputEvent>, _shape_idx: i64) {
        if self.is_resolved {
            return;
        }

        if let Ok(mouse_event) = event.try_cast::<InputEventMouseButton>() {
            if mouse_event.is_pressed() && mouse_event.get_button_index() == MouseButton::LEFT {
                self.take_damage();
            }
        }
    }

    fn take_damage(&mut self) {
        self.current_health -= 1;

        if let Some(sprite) = &mut self.anim_sprite {
            sprite.set_modulate(Color::RED);
        }

        let node = self.base().clone();
        let sprite_opt = self.anim_sprite.clone();

        if let Some(mut tween) = self.base_mut().create_tween() {
            tween.tween_property(
                &node.clone().upcast::<Object>(),
                "scale",
                &Vector2::new(1.2, 0.8).to_variant(),
                0.05,
            );
            tween.tween_property(
                &node.clone().upcast::<Object>(),
                "scale",
                &Vector2::ONE.to_variant(),
                0.05,
            );

            // Flash Red
            if let Some(sprite) = sprite_opt {
                if let Some(mut parallel) = tween.parallel() {
                    parallel.tween_property(
                        &sprite.upcast::<Object>(),
                        "modulate",
                        &Color::WHITE.to_variant(),
                        0.1,
                    );
                }
            }
        }

        if self.current_health <= 0 {
            self.resolve();
        }
    }

    fn resolve(&mut self) {
        self.is_resolved = true;

        if let Some(bus) = &self.event_bus {
            if let Some(data) = &self.data {
                let data_bind = data.bind();
                bus.clone()
                    .bind_mut()
                    .publish_hazard_resolved(data_bind.hazard_type.clone());
            }
        }

        let node = self.base().clone();

        if let Some(mut tween) = self.base_mut().create_tween() {
            tween.tween_property(
                &node.clone().upcast::<Object>(),
                "scale",
                &Vector2::ZERO.to_variant(),
                0.2,
            );
            tween.tween_callback(&node.callable("queue_free"));
        }
    }
}
