//! In-system gameplay module.
//!
//! Handles the system-scale view where the player is docked at a station
//! within their selected starting system. This is the entry point to actual
//! gameplay after onboarding.

mod actions;
mod docked;
mod flight;
mod flight_ui;
mod hud;
mod operations;
mod scene;
mod target;
mod ui;

use bevy::ecs::system::SystemParam;
use bevy::prelude::*;

use crate::state::AppState;

pub use docked::{DockedState, UndockEvent};
pub use flight::{FlightState, FlightControlsPlugin};
pub use scene::ActiveSystem;
pub use target::{Targetable, TargetKind, SelectedTarget, TargetSelectionPlugin};
pub use actions::{ContextualActionPlugin, ActionType, ActionTriggeredEvent, ActionUi};
pub use operations::TimedOperationPlugin;

pub struct InSystemPlugin;

impl Plugin for InSystemPlugin {
    fn build(&self, app: &mut App) {
        app.init_resource::<DockedState>()
            .init_resource::<ActiveSystem>()
            .add_event::<UndockEvent>()
            .add_plugins(FlightControlsPlugin)
            .add_plugins(TargetSelectionPlugin)
            .add_plugins(ContextualActionPlugin)
            .add_plugins(TimedOperationPlugin)
            .add_systems(
                OnEnter(AppState::InGame),
                (scene::setup_in_system_view, add_targetable_to_pois).chain(),
            )
            .add_systems(
                OnExit(AppState::InGame),
                (hud::despawn_in_system_hud, scene::despawn_in_system_scene).chain(),
            )
            .add_systems(
                Update,
                (
                    hud::sync_hud_panels,
                    ui::refresh_docked_ui,
                    flight_ui::update_flight_ui,
                    complete_operations,
                    handle_action_triggered,
                )
                    .chain()
                    .run_if(in_state(AppState::InGame)),
            );
    }
}

/// Bundles the resources/event-writers [`handle_action_triggered`] dispatches
/// through, keeping its parameter list under the system-argument threshold.
#[derive(SystemParam)]
struct ActionDispatch<'w> {
    time: Res<'w, Time>,
    durations: Res<'w, operations::OperationDurations>,
    started: EventWriter<'w, operations::OperationStartedEvent>,
    dock: EventWriter<'w, flight::DockEvent>,
}

/// Handle action triggered events from the contextual action buttons.
///
/// - **Undock** starts a timed operation; on completion [`complete_operations`]
///   fires the [`UndockEvent`] that drives the docked → flight transition.
/// - **Dock** fires a [`flight::DockEvent`] directly (instant) for the selected
///   station / habitable planet so the flight → docked transition (and the HUD
///   swap back to the station panel) is reachable.
fn handle_action_triggered(
    mut commands: Commands,
    mut events: EventReader<ActionTriggeredEvent>,
    player_query: Query<Entity, With<scene::PlayerShip>>,
    selected_target_query: Query<&SelectedTarget, With<scene::PlayerShip>>,
    mut dispatch: ActionDispatch,
) {
    let Ok(player_entity) = player_query.single() else {
        return;
    };
    let now_ms = dispatch.time.elapsed_secs_f64() * 1000.0;

    for event in events.read() {
        bevy::log::info!("Action triggered: {:?}", event.action_type);

        match event.action_type {
            ActionType::Undock => {
                operations::start_undocking(
                    &mut commands,
                    player_entity,
                    &dispatch.durations,
                    &mut dispatch.started,
                    now_ms,
                );
            }
            ActionType::Approach => {
                if let Ok(selected) = selected_target_query.single() {
                    operations::start_travel(
                        &mut commands,
                        player_entity,
                        selected.name.clone(),
                        &dispatch.durations,
                        &mut dispatch.started,
                        now_ms,
                    );
                }
            }
            ActionType::Dock => {
                if let Ok(selected) = selected_target_query.single() {
                    match selected.kind {
                        TargetKind::Station | TargetKind::HabitablePlanet => {
                            bevy::log::info!("Docking at {}", selected.name);
                            dispatch.dock.write(flight::DockEvent {
                                ship: player_entity,
                                station: selected.entity,
                            });
                        }
                        kind => {
                            bevy::log::info!("Cannot dock at {:?} target", kind);
                        }
                    }
                }
            }
            ActionType::StartMining => {
                bevy::log::info!("Start mining action triggered");
            }
            _ => {
                bevy::log::info!("Other action: {:?}", event.action_type);
            }
        }
    }
}

/// Bridge completed timed operations back into the docked ↔ flight state
/// machine. Today only undocking is wired — when the timed undock completes it
/// fires the [`UndockEvent`] consumed by [`flight::handle_undock`]. Other
/// operation kinds are logged for future work.
fn complete_operations(
    mut completed: EventReader<operations::OperationCompletedEvent>,
    docked: Res<DockedState>,
    mut undock_events: EventWriter<UndockEvent>,
) {
    for event in completed.read() {
        bevy::log::info!(
            "Operation completed: {:?} on entity {:?}",
            event.kind,
            event.entity
        );
        if let operations::OperationKind::Undocking = event.kind {
            undock_events.write(UndockEvent {
                station_entity: docked.station_entity.unwrap_or(Entity::PLACEHOLDER),
            });
        }
    }
}

/// Add Targetable components to spawned POIs (stations and asteroid belts)
/// so they can be selected by the target selection system.
fn add_targetable_to_pois(
    mut commands: Commands,
    station_query: Query<(Entity, &crate::gameplay::galaxy::Identifiable), (With<crate::gameplay::galaxy::Station>, Without<Targetable>)>,
    belt_query: Query<(Entity, &crate::gameplay::galaxy::Identifiable), (With<crate::gameplay::galaxy::AsteroidBelt>, Without<Targetable>)>,
) {
    // Add Targetable to stations
    for (entity, identifiable) in station_query.iter() {
        bevy::log::debug!("Adding Targetable component to station: {}", identifiable.display_name);
        commands.entity(entity).insert(Targetable {
            kind: TargetKind::Station,
            name: identifiable.display_name.clone(),
        });
    }

    // Add Targetable to asteroid belts
    for (entity, identifiable) in belt_query.iter() {
        bevy::log::debug!("Adding Targetable component to asteroid belt: {}", identifiable.display_name);
        commands.entity(entity).insert(Targetable {
            kind: TargetKind::AsteroidBelt,
            name: identifiable.display_name.clone(),
        });
    }
}
