//! UI for selecting the player's starting base of operations.

use bevy::{input::mouse::MouseWheel, prelude::*, ui::ScrollPosition, window::PrimaryWindow};

use super::{
    StartingBaseButton, StartingBaseDraft, StartingBaseFocusRequest, StartingBaseInputBlocker,
    StartingBaseSelection, StartingBaseSpawned,
};
use crate::gameplay::campaign::CampaignDraft;
use crate::gameplay::galaxy::{
    Difficulty, GasKind, SignalKind, StartingBaseCandidate, SystemContents,
};
use crate::state::AppState;

const PANEL_BG: Color = Color::srgb(0.05, 0.07, 0.12);
const PANEL_BORDER: Color = Color::srgb(0.25, 0.40, 0.62);
const TEXT_BRIGHT: Color = Color::srgb(0.82, 0.90, 1.0);
const TEXT_DIM: Color = Color::srgb(0.55, 0.65, 0.82);
const BUTTON_BG: Color = Color::srgb(0.10, 0.14, 0.22);
const BUTTON_SELECTED_BG: Color = Color::srgb(0.16, 0.28, 0.44);
const BUTTON_CONFIRM_BG: Color = Color::srgb(0.10, 0.28, 0.22);
const BUTTON_DISABLED_BG: Color = Color::srgb(0.08, 0.09, 0.12);
const BUTTON_CONFIRM_BORDER: Color = Color::srgb(0.30, 0.72, 0.45);

const TITLE_FONT_SIZE: f32 = 34.0;
const SUBTITLE_FONT_SIZE: f32 = 15.0;
const PANEL_TITLE_FONT_SIZE: f32 = 18.0;
const BODY_FONT_SIZE: f32 = 14.0;
const SMALL_FONT_SIZE: f32 = 12.0;
const BUTTON_FONT_SIZE: f32 = 16.0;

const SIDE_MARGIN: f32 = 16.0;
const TOP_MARGIN: f32 = 16.0;
const BOTTOM_MARGIN: f32 = 16.0;
const LEFT_PANEL_WIDTH: f32 = 320.0;
const RIGHT_PANEL_WIDTH: f32 = 420.0;
const ACTION_ROW_HEIGHT: f32 = 56.0;

#[derive(Component)]
pub struct StartingBaseScrollViewport;

#[derive(Component)]
pub struct StartingBaseScrollContent;

#[derive(Component)]
pub struct CandidateButtonBg(pub usize);

#[derive(Component)]
pub struct DetailsText;

#[derive(Component)]
pub struct ConfirmButtonBg;

#[derive(Component)]
pub struct ConfirmButtonText;

pub fn setup_starting_base_ui(mut commands: Commands, draft: Res<StartingBaseDraft>) {
    commands
        .spawn((
            Node {
                width: Val::Percent(100.0),
                height: Val::Percent(100.0),
                position_type: PositionType::Absolute,
                ..default()
            },
            StartingBaseSpawned,
        ))
        .with_children(|root| {
            spawn_candidate_panel(root, &draft.candidates);
            spawn_details_panel(root);
            spawn_action_row(root);
        });
}

fn spawn_candidate_panel(parent: &mut ChildSpawnerCommands, candidates: &[StartingBaseCandidate]) {
    parent
        .spawn(panel_node(Node {
            position_type: PositionType::Absolute,
            left: Val::Px(SIDE_MARGIN),
            top: Val::Px(TOP_MARGIN),
            bottom: Val::Px(BOTTOM_MARGIN),
            width: Val::Px(LEFT_PANEL_WIDTH),
            ..default()
        }))
        .with_children(|panel| {
            panel.spawn((
                Text::new("Choose Starting Base"),
                TextFont {
                    font_size: TITLE_FONT_SIZE,
                    ..default()
                },
                TextColor(TEXT_BRIGHT),
            ));
            panel.spawn((
                Text::new("Outer-galaxy systems only. Inspect local POIs before committing."),
                TextFont {
                    font_size: SUBTITLE_FONT_SIZE,
                    ..default()
                },
                TextColor(TEXT_DIM),
            ));
            panel.spawn((
                Text::new(format!("Candidate Systems ({})", candidates.len())),
                TextFont {
                    font_size: PANEL_TITLE_FONT_SIZE,
                    ..default()
                },
                TextColor(TEXT_BRIGHT),
            ));

            panel
                .spawn((
                    Node {
                        flex_grow: 1.0,
                        width: Val::Percent(100.0),
                        flex_direction: FlexDirection::Column,
                        row_gap: Val::Px(8.0),
                        overflow: Overflow::scroll_y(),
                        ..default()
                    },
                    ScrollPosition::default(),
                    StartingBaseScrollViewport,
                    StartingBaseScrollContent,
                ))
                .with_children(|scroll| {
                    if candidates.is_empty() {
                        scroll.spawn((
                            Text::new("No outer-galaxy systems were generated for these settings."),
                            TextFont {
                                font_size: BODY_FONT_SIZE,
                                ..default()
                            },
                            TextColor(TEXT_DIM),
                        ));
                    }

                    for (index, candidate) in candidates.iter().enumerate() {
                        spawn_candidate_button(scroll, index, candidate);
                    }
                });
        });
}

fn spawn_details_panel(parent: &mut ChildSpawnerCommands) {
    parent
        .spawn(panel_node(Node {
            position_type: PositionType::Absolute,
            right: Val::Px(SIDE_MARGIN),
            top: Val::Px(TOP_MARGIN),
            bottom: Val::Px(BOTTOM_MARGIN + ACTION_ROW_HEIGHT + 12.0),
            width: Val::Px(RIGHT_PANEL_WIDTH),
            ..default()
        }))
        .with_children(|panel| {
            panel.spawn((
                Text::new("System Inspection"),
                TextFont {
                    font_size: PANEL_TITLE_FONT_SIZE,
                    ..default()
                },
                TextColor(TEXT_BRIGHT),
            ));
            panel
                .spawn((
                    Node {
                        flex_grow: 1.0,
                        width: Val::Percent(100.0),
                        overflow: Overflow::scroll_y(),
                        ..default()
                    },
                    ScrollPosition::default(),
                    StartingBaseScrollViewport,
                    StartingBaseScrollContent,
                ))
                .with_children(|scroll| {
                    scroll.spawn((
                        Text::new("Select an outer system to inspect planets, stations, gates, resources, and anomalies."),
                        TextFont {
                            font_size: BODY_FONT_SIZE,
                            ..default()
                        },
                        TextColor(TEXT_DIM),
                        Node {
                            width: Val::Percent(100.0),
                            ..default()
                        },
                        DetailsText,
                    ));
                });
        });
}

fn spawn_action_row(parent: &mut ChildSpawnerCommands) {
    parent
        .spawn((
            Node {
                position_type: PositionType::Absolute,
                right: Val::Px(SIDE_MARGIN),
                bottom: Val::Px(BOTTOM_MARGIN),
                width: Val::Px(RIGHT_PANEL_WIDTH),
                height: Val::Px(ACTION_ROW_HEIGHT),
                flex_direction: FlexDirection::Row,
                column_gap: Val::Px(12.0),
                justify_content: JustifyContent::SpaceBetween,
                align_items: AlignItems::Center,
                ..default()
            },
            StartingBaseInputBlocker,
        ))
        .with_children(|row| {
            spawn_action_button(
                row,
                "Confirm Base",
                BUTTON_DISABLED_BG,
                BUTTON_CONFIRM_BORDER,
                StartingBaseButton::Confirm,
                Some((ConfirmButtonBg, ConfirmButtonText)),
            );
            spawn_action_button(
                row,
                "Back",
                BUTTON_BG,
                PANEL_BORDER,
                StartingBaseButton::Back,
                None,
            );
        });
}

fn panel_node(
    mut node: Node,
) -> (
    Node,
    BackgroundColor,
    BorderColor,
    BorderRadius,
    StartingBaseInputBlocker,
) {
    node.flex_direction = FlexDirection::Column;
    node.row_gap = Val::Px(10.0);
    node.padding = UiRect::all(Val::Px(16.0));
    node.border = UiRect::all(Val::Px(1.0));

    (
        node,
        BackgroundColor(PANEL_BG),
        BorderColor(PANEL_BORDER),
        BorderRadius::all(Val::Px(8.0)),
        StartingBaseInputBlocker,
    )
}

fn spawn_candidate_button(
    parent: &mut ChildSpawnerCommands,
    index: usize,
    candidate: &StartingBaseCandidate,
) {
    parent
        .spawn((
            Button,
            Node {
                width: Val::Percent(100.0),
                flex_direction: FlexDirection::Column,
                align_items: AlignItems::FlexStart,
                padding: UiRect::all(Val::Px(10.0)),
                row_gap: Val::Px(3.0),
                border: UiRect::all(Val::Px(1.0)),
                ..default()
            },
            BackgroundColor(BUTTON_BG),
            BorderColor(PANEL_BORDER),
            BorderRadius::all(Val::Px(6.0)),
            StartingBaseButton::Candidate(index),
            CandidateButtonBg(index),
        ))
        .with_children(|button| {
            button.spawn((
                Text::new(candidate.name.clone()),
                TextFont {
                    font_size: BUTTON_FONT_SIZE,
                    ..default()
                },
                TextColor(TEXT_BRIGHT),
            ));
            button.spawn((
                Text::new(format!(
                    "{} | sec {:.2} | {:.0}u | {} POIs",
                    candidate.faction,
                    candidate.security,
                    candidate.distance_from_core,
                    candidate.contents.total()
                )),
                TextFont {
                    font_size: SMALL_FONT_SIZE,
                    ..default()
                },
                TextColor(TEXT_DIM),
            ));
        });
}

fn spawn_action_button(
    parent: &mut ChildSpawnerCommands,
    label: &str,
    bg: Color,
    border: Color,
    marker: StartingBaseButton,
    confirm_markers: Option<(ConfirmButtonBg, ConfirmButtonText)>,
) {
    let has_confirm_markers = confirm_markers.is_some();
    let mut entity = parent.spawn((
        Button,
        Node {
            width: Val::Px(204.0),
            height: Val::Px(48.0),
            justify_content: JustifyContent::Center,
            align_items: AlignItems::Center,
            border: UiRect::all(Val::Px(1.0)),
            ..default()
        },
        BackgroundColor(bg),
        BorderColor(border),
        BorderRadius::all(Val::Px(8.0)),
        marker,
    ));

    if let Some((button_bg, _)) = confirm_markers {
        entity.insert(button_bg);
    }

    entity.with_children(|button| {
        let mut text = button.spawn((
            Text::new(label),
            TextFont {
                font_size: BUTTON_FONT_SIZE,
                ..default()
            },
            TextColor(TEXT_BRIGHT),
        ));
        if has_confirm_markers {
            text.insert(ConfirmButtonText);
        }
    });
}

pub fn scroll_starting_base_panels(
    mut scroll_events: EventReader<MouseWheel>,
    primary_window: Query<&Window, With<PrimaryWindow>>,
    viewport_nodes: Query<(&ComputedNode, &GlobalTransform), With<StartingBaseScrollViewport>>,
    mut scroll_content: Query<
        (&GlobalTransform, &mut ScrollPosition),
        With<StartingBaseScrollContent>,
    >,
) {
    let Ok(window) = primary_window.single() else {
        scroll_events.clear();
        return;
    };

    let Some(cursor_pos) = window.cursor_position() else {
        scroll_events.clear();
        return;
    };

    let hovered = viewport_nodes
        .iter()
        .find_map(|(node, transform)| cursor_over_node(cursor_pos, node, transform));

    let Some(hovered_position) = hovered else {
        return;
    };

    for (transform, mut scroll_pos) in &mut scroll_content {
        if transform.translation().distance(hovered_position) > 0.5 {
            continue;
        }
        for event in scroll_events.read() {
            scroll_pos.offset_y -= event.y * 20.0;
        }
        return;
    }
}

fn cursor_over_node(
    cursor_pos: Vec2,
    node: &ComputedNode,
    transform: &GlobalTransform,
) -> Option<Vec3> {
    let position = transform.translation();
    let center = position.truncate();
    let half = node.size() * 0.5;
    let min = center - half;
    let max = center + half;

    if cursor_pos.x >= min.x
        && cursor_pos.x <= max.x
        && cursor_pos.y >= min.y
        && cursor_pos.y <= max.y
    {
        Some(position)
    } else {
        None
    }
}

pub fn candidate_button_handler(
    mut commands: Commands,
    mut draft: ResMut<StartingBaseDraft>,
    query: Query<(&Interaction, &StartingBaseButton), Changed<Interaction>>,
) {
    for (interaction, button) in &query {
        if *interaction != Interaction::Pressed {
            continue;
        }
        if let StartingBaseButton::Candidate(index) = *button {
            if index < draft.candidates.len() {
                draft.selected_index = Some(index);
                commands.insert_resource(StartingBaseFocusRequest {
                    candidate_index: index,
                });
            }
        }
    }
}

pub fn refresh_starting_base_ui(
    draft: Res<StartingBaseDraft>,
    mut texts: ParamSet<(
        Query<&mut Text, With<DetailsText>>,
        Query<&mut Text, With<ConfirmButtonText>>,
    )>,
    mut backgrounds: ParamSet<(
        Query<(&CandidateButtonBg, &mut BackgroundColor)>,
        Query<&mut BackgroundColor, With<ConfirmButtonBg>>,
    )>,
) {
    if !draft.is_changed() {
        return;
    }

    {
        let mut candidate_bgs = backgrounds.p0();
        for (marker, mut bg) in &mut candidate_bgs {
            bg.0 = if Some(marker.0) == draft.selected_index {
                BUTTON_SELECTED_BG
            } else {
                BUTTON_BG
            };
        }
    }

    {
        let mut details_text = texts.p0();
        if let Ok(mut text) = details_text.single_mut() {
            text.0 = match selected_candidate(&draft) {
                Some(candidate) => format_candidate_details(candidate),
                None => "Select an outer system to inspect planets, stations, gates, resources, and anomalies.".to_string(),
            };
        }
    }

    let has_selection = draft.selected_index.is_some();
    {
        let mut confirm_bg = backgrounds.p1();
        if let Ok(mut bg) = confirm_bg.single_mut() {
            bg.0 = if has_selection {
                BUTTON_CONFIRM_BG
            } else {
                BUTTON_DISABLED_BG
            };
        }
    }
    {
        let mut confirm_text = texts.p1();
        if let Ok(mut text) = confirm_text.single_mut() {
            text.0 = if has_selection {
                "Confirm Base".to_string()
            } else {
                "Select a Base".to_string()
            };
        }
    }
}

pub fn action_button_handler(
    mut commands: Commands,
    mut next_state: ResMut<NextState<AppState>>,
    draft: Res<StartingBaseDraft>,
    mut campaign: ResMut<CampaignDraft>,
    query: Query<(&Interaction, &StartingBaseButton), Changed<Interaction>>,
) {
    for (interaction, button) in &query {
        if *interaction != Interaction::Pressed {
            continue;
        }

        match button {
            StartingBaseButton::Confirm => {
                let Some(candidate) = selected_candidate(&draft) else {
                    continue;
                };
                let selection = StartingBaseSelection {
                    system_id: candidate.id.clone(),
                    system_name: candidate.name.clone(),
                    faction: candidate.faction,
                    security: candidate.security,
                };
                bevy::log::info!(
                    "Selected starting base: id={}, name={}, faction={}, security={:.2}, poi_count={}",
                    selection.system_id,
                    selection.system_name,
                    selection.faction,
                    selection.security,
                    candidate.contents.total(),
                );
                campaign.save_starting_base(selection.clone());
                commands.insert_resource(selection);
                next_state.set(AppState::InGame);
            }
            StartingBaseButton::Back => {
                next_state.set(AppState::CharacterCreation);
            }
            StartingBaseButton::Candidate(_) => {}
        }
    }
}

fn selected_candidate(draft: &StartingBaseDraft) -> Option<&StartingBaseCandidate> {
    draft
        .selected_index
        .and_then(|index| draft.candidates.get(index))
}

fn format_candidate_details(candidate: &StartingBaseCandidate) -> String {
    let mut lines = vec![
        candidate.name.clone(),
        format!(
            "System ID: {} | Faction: {} | Security: {:.2} | Distance: {:.0}u",
            candidate.id, candidate.faction, candidate.security, candidate.distance_from_core
        ),
        format!("Total POIs: {}", candidate.contents.total()),
        String::new(),
    ];

    append_contents(&mut lines, &candidate.contents);
    lines.join("\n")
}

fn append_contents(lines: &mut Vec<String>, contents: &SystemContents) {
    lines.push(format!("Planets ({})", contents.planets.len()));
    for planet in &contents.planets {
        let population = if planet.population > 0 {
            format!("pop {}", planet.population)
        } else {
            "uninhabited".to_string()
        };
        lines.push(format!(
            "- {}: {}, orbit {:.1}, {}",
            planet.name,
            planet.planet_type.display_name(),
            planet.orbit,
            population
        ));
    }

    lines.push(String::new());
    lines.push(format!("Stations ({})", contents.stations.len()));
    for station in &contents.stations {
        lines.push(format!(
            "- {}: orbit {:.1}, pop {}",
            station.name, station.orbit, station.population
        ));
    }

    lines.push(String::new());
    lines.push(format!("Asteroid Belts ({})", contents.belts.len()));
    for belt in &contents.belts {
        lines.push(format!(
            "- {}: orbit {:.1}-{:.1}, yield {:.0}",
            belt.name, belt.inner_orbit, belt.outer_orbit, belt.yield_remaining
        ));
    }

    lines.push(String::new());
    lines.push(format!("Gas Clouds ({})", contents.gas_clouds.len()));
    for cloud in &contents.gas_clouds {
        lines.push(format!(
            "- {}: {}, flow {:.1}/s",
            cloud.name,
            gas_kind_label(cloud.gas_kind),
            cloud.flow_rate
        ));
    }

    lines.push(String::new());
    lines.push(format!("Anomalies ({})", contents.anomalies.len()));
    for anomaly in &contents.anomalies {
        let probe_text = if anomaly.requires_probe {
            "probe required"
        } else {
            "ship sensors"
        };
        lines.push(format!(
            "- {}: {}, {}, {}",
            anomaly.name,
            difficulty_label(anomaly.difficulty),
            signal_kind_label(anomaly.signal_kind),
            probe_text
        ));
    }

    lines.push(String::new());
    lines.push(format!("Stargates ({})", contents.stargates.len()));
    for gate in &contents.stargates {
        lines.push(format!(
            "- {}: destination {}",
            gate.name, gate.destination_name
        ));
    }
}

fn difficulty_label(difficulty: Difficulty) -> &'static str {
    match difficulty {
        Difficulty::Trivial => "trivial",
        Difficulty::Easy => "easy",
        Difficulty::Moderate => "moderate",
        Difficulty::Hard => "hard",
        Difficulty::Expert => "expert",
    }
}

fn signal_kind_label(signal_kind: SignalKind) -> &'static str {
    match signal_kind {
        SignalKind::Infrared => "infrared",
        SignalKind::Gravimetric => "gravimetric",
        SignalKind::Radar => "radar",
        SignalKind::Magnetometric => "magnetometric",
    }
}

fn gas_kind_label(gas_kind: GasKind) -> &'static str {
    match gas_kind {
        GasKind::Hydrogen => "hydrogen",
        GasKind::Helium => "helium",
        GasKind::Nitrogen => "nitrogen",
        GasKind::Exotic => "exotic",
    }
}
