use crate::{
    consts, core::event_bus::EventBus, data::day_config::DayConfig,
    systems::hazard_system::HazardSystem,
};
use godot::{
    classes::{Control, Label},
    prelude::*,
};

#[derive(GodotClass)]
#[class(base=Node)]
pub struct GameManager {
    #[export]
    days: Array<Gd<DayConfig>>,

    #[export]
    hazard_system: Option<Gd<HazardSystem>>,
    #[export]
    minigame_container: Option<Gd<Node>>,

    #[export]
    win_screen: Option<Gd<Control>>,
    #[export]
    lose_screen: Option<Gd<Control>>,
    #[export]
    game_complete_screen: Option<Gd<Control>>,

    #[export]
    day_label: Option<Gd<Label>>,
    #[export]
    main_menu_scene: Option<Gd<PackedScene>>,

    current_day_index: i32,
    current_mini_game: Option<Gd<Node>>,

    event_bus: Option<Gd<EventBus>>,

    base: Base<Node>,
}

#[godot_api]
impl INode for GameManager {
    fn init(base: Base<Node>) -> Self {
        Self {
            days: Array::new(),
            hazard_system: None,
            minigame_container: None,
            win_screen: None,
            lose_screen: None,
            game_complete_screen: None,
            day_label: None,
            main_menu_scene: None,
            current_day_index: 0,
            current_mini_game: None,
            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_COMPLETED,
            &self.base().callable("handle_lift_result"),
        );

        self.event_bus = Some(bus);

        let idx = self.current_day_index;
        self.base_mut()
            .call_deferred("start_day", &[idx.to_variant()]);
    }
}

#[godot_api]
impl GameManager {
    #[func]
    fn start_day(&mut self, index: i32) {
        if index >= self.days.len() as i32 {
            godot_print!("GameManager: All days complete!");

            self.cleanup_level();

            if let Some(s) = &mut self.win_screen {
                s.set_visible(false);
            }
            if let Some(s) = &mut self.lose_screen {
                s.set_visible(false);
            }

            if let Some(s) = &mut self.game_complete_screen {
                s.set_visible(true);
            }

            return;
        }

        self.cleanup_level();

        let config = self.days.get(index as usize).expect("Invalid Day Index");
        let config_bind = config.bind();

        if let Some(prefab) = &config_bind.mini_game_scene {
            if let Some(instance) = prefab.instantiate() {
                if let Some(container) = &mut self.minigame_container {
                    container.add_child(&instance.clone());
                }

                if let Some(mut system) = instance.get_node_or_null("System") {
                    system.set("target_value", &config_bind.target_weight.to_variant());
                    system.set("gravity", &config_bind.gravity.to_variant());
                }
                self.current_mini_game = Some(instance);
            }
        }

        if let Some(hs) = &mut self.hazard_system {
            hs.bind_mut()
                .set_available_hazards(config_bind.available_hazards.clone());
        }

        if let Some(bus) = &mut self.event_bus {
            bus.clone().bind_mut().publish_day_started(index);
        }

        if let Some(l) = &mut self.day_label {
            l.set_text(&config_bind.day_title);
        }
        if let Some(s) = &mut self.win_screen {
            s.set_visible(false);
        }
        if let Some(s) = &mut self.lose_screen {
            s.set_visible(false);
        }
    }

    fn cleanup_level(&mut self) {
        if let Some(game) = &mut self.current_mini_game {
            game.queue_free();
            self.current_mini_game = None;
        }
    }

    fn handle_win(&mut self) {
        if let Some(s) = &mut self.win_screen {
            s.set_visible(true);
        }
    }

    fn handle_loss(&mut self) {
        if let Some(s) = &mut self.lose_screen {
            s.set_visible(true);
        }
    }

    fn next_day(&mut self) {
        self.current_day_index += 1;
        self.start_day(self.current_day_index);
    }

    fn restart_day(&mut self) {
        self.base().get_tree().unwrap().reload_current_scene();
    }

    #[func]
    fn handle_lift_result(&mut self, success: bool) {
        if success {
            self.handle_win();
        } else {
            self.handle_loss();
        }
    }

    #[func]
    pub fn on_next_day_pressed(&mut self) {
        self.next_day();
    }

    #[func]
    pub fn on_retry_pressed(&mut self) {
        self.restart_day();
    }

    #[func]
    pub fn on_menu_pressed(&mut self) {
        if let Some(menu) = &self.main_menu_scene {
            let menu_ref = menu.clone();
            self.base()
                .get_tree()
                .unwrap()
                .change_scene_to_packed(&menu_ref);
        } else {
            self.base().get_tree().unwrap().quit();
        }
    }
}
