use crate::{consts, core::event_bus::EventBus};
use godot::prelude::*;

#[derive(GodotClass)]
#[class(base=Node)]
pub struct DeadliftSystem {
    #[export]
    power_per_click: f32,
    #[export]
    gravity: f32,
    #[export]
    target_value: f32,

    #[export]
    bar_height: f32,
    #[export]
    hold_zone_threshold: f32,
    #[export]
    bar_visual: Option<Gd<Node2D>>,
    #[export]
    start_pos: Vector2,
    #[export]
    end_pos: Vector2,

    current_bar_height: f32,
    hold_timer: f32,
    is_lift_complete: bool,
    active_hazard_count: i32,
    event_bus: Option<Gd<EventBus>>,

    base: Base<Node>,
}

#[godot_api]
impl INode for DeadliftSystem {
    fn init(base: Base<Node>) -> Self {
        Self {
            power_per_click: 5.0,
            gravity: 2.0,
            target_value: 5.0,

            bar_height: 100.0,
            hold_zone_threshold: 0.8,
            bar_visual: None,
            start_pos: Vector2::ZERO,
            end_pos: Vector2::ZERO,

            current_bar_height: 0.0,
            hold_timer: 0.0,
            active_hazard_count: 0,
            is_lift_complete: false,
            event_bus: None,
            base,
        }
    }

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

        bus.connect(
            consts::LIFT_EFFORT_APPLIED,
            &self.base().callable("on_lift_effort"),
        );
        bus.connect(
            consts::HAZARD_SPAWNED,
            &self.base().callable("on_hazard_spawned"),
        );
        bus.connect(
            consts::HAZARD_RESOLVED,
            &self.base().callable("on_hazard_resolved"),
        );
        bus.connect(
            consts::LIFT_COMPLETED,
            &self.base().callable("on_lift_completed"),
        );

        self.event_bus = Some(bus);
    }

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

        let bus = match &mut self.event_bus {
            Some(bus) => bus,
            None => return,
        };

        let dt = delta as f32;

        if self.current_bar_height > 0.0 {
            self.current_bar_height -= self.gravity * dt;
            self.current_bar_height = self.current_bar_height.max(0.0);
        }

        let visual_ratio = self.current_bar_height / self.bar_height;

        if visual_ratio >= self.hold_zone_threshold && self.active_hazard_count == 0 {
            self.hold_timer += dt;

            bus.clone().bind_mut().publish_camera_trauma(0.15 * dt);
        }

        bus.clone()
            .bind_mut()
            .publish_lift_progress(self.hold_timer / self.target_value);
        bus.clone()
            .bind_mut()
            .publish_lift_visual_height(visual_ratio);

        if let Some(visual) = &mut self.bar_visual {
            let new_pos = self.start_pos.lerp(self.end_pos, visual_ratio);
            visual.set_position(new_pos);
        }

        if self.hold_timer >= self.target_value {
            bus.clone().bind_mut().publish_camera_trauma(1.0);

            bus.call_deferred("publish_lift_completed", &[true.to_variant()]);
            self.is_lift_complete = true;
        }
    }
}

#[godot_api]
impl DeadliftSystem {
    #[func]
    fn on_lift_effort(&mut self, delta: f32) {
        if self.is_lift_complete || self.active_hazard_count > 0 {
            return;
        }

        self.current_bar_height += self.power_per_click * delta;
        self.current_bar_height = self.current_bar_height.min(self.bar_height);
    }

    #[func]
    fn on_lift_completed(&mut self, _success: bool) {
        self.is_lift_complete = true;
    }

    #[func]
    fn on_hazard_spawned(&mut self, _type: String) {
        self.active_hazard_count += 1;
    }

    #[func]
    fn on_hazard_resolved(&mut self, _type: String) {
        self.active_hazard_count -= 1;
        if self.active_hazard_count < 0 {
            self.active_hazard_count = 0;
        }
    }
}
