//! Character creation UI: a sticky header, scrollable form, and sticky
//! Confirm / Back buttons.
//!
//! Each picker is a row of `[<]  label  value  [>]`. The `[<]` and `[>]`
//! buttons carry a [`PickerButton`] marker identifying which field to cycle
//! and in which direction; [`refresh_picker_values`] patches the displayed
//! `Text` from [`super::params::CharacterDraft`] every frame so the panels
//! stay in sync without re-spawning.
//!
//! Visual language mirrors the galaxy control panel
//! ([`crate::gameplay::galaxy::ui`]): dark navy panels, cyan borders, dim
//! labels, bright values.

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

use super::{CharacterCreationButton, CharacterCreationSpawned};
use crate::gameplay::campaign::CampaignDraft;
use crate::gameplay::character_creation::params::{
    CharacterDraft, BACKSTORY_QUESTIONS, BACKSTORY_QUESTION_COUNT, CHARACTER_NAMES, ORIGINS,
    STARTING_SHIPS,
};

// ── Styling constants ───────────────────────────────────────────────────────

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 ACCENT: Color = Color::srgb(0.32, 0.58, 0.92);
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 TEXT_FADED: Color = Color::srgb(0.42, 0.52, 0.68);
const BUTTON_BG: Color = Color::srgb(0.10, 0.14, 0.22);
const BUTTON_BG_CONFIRM: Color = Color::srgb(0.10, 0.28, 0.22);
const BUTTON_BORDER_CONFIRM: Color = Color::srgb(0.30, 0.72, 0.45);

const TITLE_FONT_SIZE: f32 = 36.0;
const SUBTITLE_FONT_SIZE: f32 = 16.0;
const SECTION_FONT_SIZE: f32 = 13.0;
const LABEL_FONT_SIZE: f32 = 15.0;
const VALUE_FONT_SIZE: f32 = 17.0;
const BLURB_FONT_SIZE: f32 = 13.0;
const BUTTON_FONT_SIZE: f32 = 18.0;
const ARROW_FONT_SIZE: f32 = 20.0;

const ARROW_BUTTON: f32 = 36.0;
const CARD_WIDTH: f32 = 760.0;

// ── Markers ─────────────────────────────────────────────────────────────────

/// Identifies which draft field a `Picker [<] [>]` button mutates, and the
/// direction. Stored as a small enum so a single system can handle every
/// arrow button in one pass.
#[derive(Component, Clone, Copy)]
pub enum PickerButton {
    Name(i32),
    Origin(i32),
    Backstory(usize, i32),
    Ship(i32),
}

/// Tag on the `Text` node that renders the current name value, so
/// [`refresh_picker_values`] can find and patch it without re-spawning.
#[derive(Component)]
pub struct NameValue;

#[derive(Component)]
pub struct OriginValue;

#[derive(Component)]
pub struct ShipValue;

/// Blurb blocks under each picker — refreshed together with the value.
#[derive(Component)]
pub struct OriginBlurb;

#[derive(Component)]
pub struct ShipBlurb;

#[derive(Component)]
pub struct OriginBonus;

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

#[derive(Component)]
pub struct StatsSummary;

#[derive(Component)]
pub struct CharacterCreationScrollViewport;

#[derive(Component)]
pub struct CharacterCreationScrollContent;

// ── Setup ───────────────────────────────────────────────────────────────────

pub fn setup_character_creation_ui(mut commands: Commands) {
    commands
        .spawn((
            Node {
                width: Val::Percent(100.0),
                height: Val::Percent(100.0),
                display: Display::Flex,
                flex_direction: FlexDirection::Column,
                justify_content: JustifyContent::FlexStart,
                align_items: AlignItems::Center,
                padding: UiRect::all(Val::Px(24.0)),
                ..default()
            },
            BackgroundColor(Color::srgb(0.02, 0.02, 0.06)),
            CharacterCreationSpawned,
        ))
        .with_children(|root| {
            // ── Title block ──────────────────────────────────────────────
            root.spawn((
                Text::new("Character Creation"),
                TextFont {
                    font_size: TITLE_FONT_SIZE,
                    ..default()
                },
                TextColor(TEXT_BRIGHT),
            ));
            root.spawn((
                Text::new(
                    "Pick a name, trace your backstory, choose a ship, and confirm to begin.",
                ),
                TextFont {
                    font_size: SUBTITLE_FONT_SIZE,
                    ..default()
                },
                TextColor(TEXT_DIM),
                Node {
                    margin: UiRect::bottom(Val::Px(12.0)),
                    ..default()
                },
            ));

            // ── Scrollable form ─────────────────────────────────────────
            root.spawn((
                Node {
                    width: Val::Percent(100.0),
                    flex_grow: 1.0,
                    flex_direction: FlexDirection::Column,
                    align_items: AlignItems::Center,
                    overflow: Overflow::scroll_y(),
                    padding: UiRect::vertical(Val::Px(4.0)),
                    ..default()
                },
                ScrollPosition::default(),
                CharacterCreationScrollViewport,
                CharacterCreationScrollContent,
            ))
            .with_children(|scroll| {
                scroll
                    .spawn((
                        Node {
                            width: Val::Percent(100.0),
                            max_width: Val::Px(CARD_WIDTH),
                            padding: UiRect::all(Val::Px(20.0)),
                            flex_direction: FlexDirection::Column,
                            row_gap: Val::Px(12.0),
                            border: UiRect::all(Val::Px(1.0)),
                            ..default()
                        },
                        BackgroundColor(PANEL_BG),
                        BorderColor(PANEL_BORDER),
                        BorderRadius::all(Val::Px(10.0)),
                    ))
                    .with_children(|card| {
                        spawn_section_label(card, "NAME");
                        spawn_picker_row(
                            card,
                            "Callsign",
                            PickerButton::Name(-1),
                            PickerButton::Name(1),
                        );
                        spawn_spacer(card);

                        spawn_section_label(card, "ORIGIN");
                        spawn_picker_row_with_marker(
                            card,
                            "Background",
                            PickerButton::Origin(-1),
                            PickerButton::Origin(1),
                            OriginValue,
                        );
                        card.spawn((
                            Text::new(""),
                            TextFont {
                                font_size: BLURB_FONT_SIZE,
                                ..default()
                            },
                            TextColor(TEXT_DIM),
                            Node {
                                width: Val::Percent(100.0),
                                ..default()
                            },
                            OriginBlurb,
                        ));
                        card.spawn((
                            Text::new(""),
                            TextFont {
                                font_size: BLURB_FONT_SIZE,
                                ..default()
                            },
                            TextColor(ACCENT),
                            Node {
                                width: Val::Percent(100.0),
                                margin: UiRect::top(Val::Px(2.0)),
                                ..default()
                            },
                            OriginBonus,
                        ));
                        spawn_spacer(card);

                        spawn_section_label(card, "BACKSTORY");
                        spawn_backstory_rows(card);
                        card.spawn((
                            Text::new(""),
                            TextFont {
                                font_size: BLURB_FONT_SIZE,
                                ..default()
                            },
                            TextColor(ACCENT),
                            Node {
                                width: Val::Percent(100.0),
                                margin: UiRect::top(Val::Px(2.0)),
                                ..default()
                            },
                            StatsSummary,
                        ));
                        spawn_spacer(card);

                        spawn_section_label(card, "STARTING SHIP");
                        spawn_picker_row_with_marker(
                            card,
                            "Hull",
                            PickerButton::Ship(-1),
                            PickerButton::Ship(1),
                            ShipValue,
                        );
                        card.spawn((
                            Text::new(""),
                            TextFont {
                                font_size: BLURB_FONT_SIZE,
                                ..default()
                            },
                            TextColor(TEXT_DIM),
                            Node {
                                width: Val::Percent(100.0),
                                ..default()
                            },
                            ShipBlurb,
                        ));
                    });
            });

            // ── Button row ──────────────────────────────────────────────
            root.spawn(Node {
                width: Val::Percent(100.0),
                max_width: Val::Px(CARD_WIDTH),
                flex_direction: FlexDirection::Row,
                flex_wrap: FlexWrap::Wrap,
                justify_content: JustifyContent::Center,
                column_gap: Val::Px(16.0),
                row_gap: Val::Px(8.0),
                margin: UiRect::top(Val::Px(12.0)),
                ..default()
            })
            .with_children(|row| {
                spawn_action_button(
                    row,
                    "Confirm",
                    BUTTON_BG_CONFIRM,
                    BUTTON_BORDER_CONFIRM,
                    CharacterCreationButton::Confirm,
                );
                spawn_action_button(
                    row,
                    "Back",
                    BUTTON_BG,
                    PANEL_BORDER,
                    CharacterCreationButton::Back,
                );
            });

            // ── Footer hint ─────────────────────────────────────────────
            root.spawn((
                Text::new("Esc to go back"),
                TextFont {
                    font_size: BLURB_FONT_SIZE,
                    ..default()
                },
                TextColor(TEXT_FADED),
            ));
        });
}

// ── Scroll input ───────────────────────────────────────────────────────────

/// Route mouse-wheel events to the form's scroll container when the cursor is
/// over it. Bevy 0.16 does not wire `MouseWheel` to `ScrollPosition`
/// automatically, so this mirrors the galaxy panel bridge.
pub fn scroll_character_creation_form(
    mut scroll_events: EventReader<MouseWheel>,
    primary_window: Query<&Window, With<PrimaryWindow>>,
    viewport_nodes: Query<(&ComputedNode, &GlobalTransform), With<CharacterCreationScrollViewport>>,
    mut scroll_content: Query<&mut ScrollPosition, With<CharacterCreationScrollContent>>,
) {
    let Ok(window) = primary_window.single() else {
        scroll_events.clear();
        return;
    };

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

    let over_viewport = viewport_nodes.iter().any(|(node, transform)| {
        let pos = transform.translation().truncate();
        let size = node.size();
        let min = pos - size * 0.5;
        let max = pos + size * 0.5;
        cursor_pos.x >= min.x
            && cursor_pos.x <= max.x
            && cursor_pos.y >= min.y
            && cursor_pos.y <= max.y
    });

    if !over_viewport {
        return;
    }

    let Ok(mut scroll_pos) = scroll_content.single_mut() else {
        return;
    };

    for event in scroll_events.read() {
        scroll_pos.offset_y -= event.y * 20.0;
    }
}

// ── Sub-spawners ────────────────────────────────────────────────────────────

fn spawn_section_label(parent: &mut ChildSpawnerCommands, label: &str) {
    parent.spawn((
        Text::new(label),
        TextFont {
            font_size: SECTION_FONT_SIZE,
            ..default()
        },
        TextColor(TEXT_FADED),
        Node {
            margin: UiRect::bottom(Val::Px(2.0)),
            ..default()
        },
    ));
}

fn spawn_spacer(parent: &mut ChildSpawnerCommands) {
    parent.spawn(Node {
        height: Val::Px(4.0),
        ..default()
    });
}

/// `[<]  label   value  [>]` row with the default value marker — used for
/// the name picker, which has no blurb and uses the generic `NameValue`.
fn spawn_picker_row(
    parent: &mut ChildSpawnerCommands,
    label: &str,
    decr: PickerButton,
    incr: PickerButton,
) {
    spawn_picker_row_with_marker(parent, label, decr, incr, NameValue);
}

/// Same as [`spawn_picker_row`] but lets the caller pick which value marker
/// to attach (name vs origin vs ship).
fn spawn_picker_row_with_marker<M: Component>(
    parent: &mut ChildSpawnerCommands,
    label: &str,
    decr: PickerButton,
    incr: PickerButton,
    value_marker: M,
) {
    parent
        .spawn(Node {
            width: Val::Percent(100.0),
            flex_direction: FlexDirection::Row,
            align_items: AlignItems::Center,
            column_gap: Val::Px(10.0),
            ..default()
        })
        .with_children(|row| {
            // [<]
            spawn_arrow_button(row, "‹", decr);
            // label
            row.spawn((
                Text::new(label),
                TextFont {
                    font_size: LABEL_FONT_SIZE,
                    ..default()
                },
                TextColor(TEXT_DIM),
                Node {
                    width: Val::Px(110.0),
                    ..default()
                },
            ));
            // value — flexible width so it grows to fill
            row.spawn((
                Text::new("—"),
                TextFont {
                    font_size: VALUE_FONT_SIZE,
                    ..default()
                },
                TextColor(TEXT_BRIGHT),
                Node {
                    flex_grow: 1.0,
                    ..default()
                },
                value_marker,
            ));
            // [+]
            spawn_arrow_button(row, "›", incr);
        });
}

fn spawn_backstory_rows(parent: &mut ChildSpawnerCommands) {
    for (question_index, question) in BACKSTORY_QUESTIONS.iter().enumerate() {
        spawn_picker_row_with_marker(
            parent,
            question.prompt,
            PickerButton::Backstory(question_index, -1),
            PickerButton::Backstory(question_index, 1),
            BackstoryValue(question_index),
        );
    }
}

fn spawn_arrow_button(parent: &mut ChildSpawnerCommands, glyph: &str, marker: PickerButton) {
    parent
        .spawn((
            Button,
            Node {
                width: Val::Px(ARROW_BUTTON),
                height: Val::Px(ARROW_BUTTON),
                justify_content: JustifyContent::Center,
                align_items: AlignItems::Center,
                border: UiRect::all(Val::Px(1.0)),
                ..default()
            },
            BackgroundColor(BUTTON_BG),
            BorderColor(PANEL_BORDER),
            BorderRadius::all(Val::Px(6.0)),
            marker,
        ))
        .with_children(|btn| {
            btn.spawn((
                Text::new(glyph),
                TextFont {
                    font_size: ARROW_FONT_SIZE,
                    ..default()
                },
                TextColor(TEXT_BRIGHT),
            ));
        });
}

fn spawn_action_button(
    parent: &mut ChildSpawnerCommands,
    label: &str,
    bg: Color,
    border: Color,
    marker: CharacterCreationButton,
) {
    parent
        .spawn((
            Button,
            Node {
                width: Val::Px(200.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,
        ))
        .with_children(|btn| {
            btn.spawn((
                Text::new(label),
                TextFont {
                    font_size: BUTTON_FONT_SIZE,
                    ..default()
                },
                TextColor(TEXT_BRIGHT),
            ));
        });
}

// ── Refresh ─────────────────────────────────────────────────────────────────

/// Patch every dynamic `Text` child from the current draft. Cheap (≤6 nodes)
/// and avoids re-spawning the whole panel on every selection change.
///
/// Each `Text` node carries exactly one of the marker components below; the
/// `Without<>` filters keep the queries disjoint so `.single_mut()` can never
/// hit an ambiguous match. Bevy's Query tuples are inherently verbose —
/// `clippy::type_complexity` is allowed on this function by convention.
#[allow(clippy::type_complexity)]
pub fn refresh_picker_values(
    draft: Res<CharacterDraft>,
    mut text_queries: ParamSet<(
        Query<&mut Text, With<NameValue>>,
        Query<&mut Text, With<OriginValue>>,
        Query<&mut Text, With<OriginBlurb>>,
        Query<&mut Text, With<OriginBonus>>,
        Query<(&BackstoryValue, &mut Text)>,
        Query<&mut Text, With<StatsSummary>>,
        Query<&mut Text, With<ShipValue>>,
        Query<&mut Text, With<ShipBlurb>>,
    )>,
) {
    // The draft only changes when a picker button is pressed; if the resource
    // hasn't been mutated, we can skip the whole pass.
    if !draft.is_changed() {
        return;
    }

    {
        let mut name_q = text_queries.p0();
        if let Ok(mut txt) = name_q.single_mut() {
            if txt.0 != draft.name() {
                txt.0 = draft.name().to_string();
            }
        }
    }

    let origin = draft.origin();
    {
        let mut origin_q = text_queries.p1();
        if let Ok(mut txt) = origin_q.single_mut() {
            if txt.0 != origin.name {
                txt.0 = origin.name.to_string();
            }
        }
    }
    {
        let mut origin_blurb_q = text_queries.p2();
        if let Ok(mut txt) = origin_blurb_q.single_mut() {
            if txt.0 != origin.blurb {
                txt.0 = origin.blurb.to_string();
            }
        }
    }
    {
        let mut origin_bonus_q = text_queries.p3();
        if let Ok(mut txt) = origin_bonus_q.single_mut() {
            if txt.0 != origin.bonus {
                txt.0 = origin.bonus.to_string();
            }
        }
    }

    {
        let mut backstory_q = text_queries.p4();
        for (marker, mut txt) in &mut backstory_q {
            let choice = draft.backstory_choice(marker.0);
            if txt.0 != choice.name {
                txt.0 = choice.name.to_string();
            }
        }
    }

    {
        let mut stats_q = text_queries.p5();
        if let Ok(mut txt) = stats_q.single_mut() {
            let summary = draft.stats().summary();
            if txt.0 != summary {
                txt.0 = summary;
            }
        }
    }

    let ship = draft.ship();
    {
        let mut ship_q = text_queries.p6();
        if let Ok(mut txt) = ship_q.single_mut() {
            let label = format!("{}  ·  {}", ship.name, ship.role);
            if txt.0 != label {
                txt.0 = label;
            }
        }
    }
    {
        let mut ship_blurb_q = text_queries.p7();
        if let Ok(mut txt) = ship_blurb_q.single_mut() {
            if txt.0 != ship.blurb {
                txt.0 = ship.blurb.to_string();
            }
        }
    }
}

// ── Button handlers ─────────────────────────────────────────────────────────

/// Handle every picker `[<] [>]` press: cycle the matching draft field by the
/// button's signed delta. The next frame's [`refresh_picker_values`] will
/// propagate the new value into the UI.
pub fn picker_button_handler(
    mut draft: ResMut<CharacterDraft>,
    query: Query<(&Interaction, &PickerButton), Changed<Interaction>>,
) {
    for (interaction, button) in &query {
        if *interaction != Interaction::Pressed {
            continue;
        }
        match *button {
            PickerButton::Name(delta) => draft.cycle_name(delta),
            PickerButton::Origin(delta) => draft.cycle_origin(delta),
            PickerButton::Backstory(question_index, delta) => {
                draft.cycle_backstory(question_index, delta);
            }
            PickerButton::Ship(delta) => draft.cycle_ship(delta),
        }
    }
}

/// Confirm / Back buttons. Confirm advances to starting base selection; Back
/// returns to `AppState::Galaxy`. Kept in a single system since neither needs
/// extra resources — the draft is persisted later by the (yet-to-be-written)
/// save pipeline.
pub fn action_button_handler(
    mut next_state: ResMut<NextState<crate::state::AppState>>,
    draft: Res<CharacterDraft>,
    mut campaign: ResMut<CampaignDraft>,
    query: Query<(&Interaction, &CharacterCreationButton), Changed<Interaction>>,
) {
    for (interaction, button) in &query {
        if *interaction != Interaction::Pressed {
            continue;
        }
        match button {
            CharacterCreationButton::Confirm => {
                bevy::log::info!(
                    "Confirming character: name={}, origin={}, ship={}, backstory={}, stats={}",
                    draft.name(),
                    draft.origin().id,
                    draft.ship().id,
                    format_backstory_log(&draft),
                    draft.stats().summary(),
                );
                campaign.save_character(*draft);
                next_state.set(crate::state::AppState::StartingBaseSelection);
            }
            CharacterCreationButton::Back => {
                next_state.set(crate::state::AppState::Galaxy);
            }
        }
    }
}

fn format_backstory_log(draft: &CharacterDraft) -> String {
    let mut entries = Vec::with_capacity(BACKSTORY_QUESTION_COUNT);
    for question_index in 0..BACKSTORY_QUESTION_COUNT {
        let choice = draft.backstory_choice(question_index);
        entries.push(format!("{}: {}", choice.id, choice.blurb));
    }
    entries.join(" | ")
}

// ── Sanity check: name/origin/ship counts must agree ───────────────────────
//
// These constants are here so a quick grep tells future-you where the UI's
// per-section expectations live. The picker code tags each value Text with
// exactly one of `NameValue` / `OriginValue` / `ShipValue`, so the layout
// must spawn exactly one of each — if you add a second value Text, the
// `.single_mut()` queries in `refresh_picker_values` will panic.

/// Compile-time assertion that the presets are non-empty. The picker's
/// modulo math assumes `len > 0`; if either list ever becomes empty by
/// mistake, this surfaces the bug at the call site rather than at runtime.
const _PRESET_NONEMPTY: () = {
    assert!(!CHARACTER_NAMES.is_empty());
    assert!(!ORIGINS.is_empty());
    assert!(!STARTING_SHIPS.is_empty());
    assert!(BACKSTORY_QUESTIONS.len() == BACKSTORY_QUESTION_COUNT);
};
