//! Character creation UI: a centered single-column layout with three pickers
//! (name / origin / starting ship) and 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::prelude::*;

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

// ── 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 PICKER_WIDTH: f32 = 520.0;
const ARROW_BUTTON: f32 = 36.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),
    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;

// ── 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::Center,
                align_items: AlignItems::Center,
                row_gap: Val::Px(18.0),
                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, an origin, and a ship. Confirm to begin."),
                TextFont {
                    font_size: SUBTITLE_FONT_SIZE,
                    ..default()
                },
                TextColor(TEXT_DIM),
                Node {
                    margin: UiRect::bottom(Val::Px(8.0)),
                    ..default()
                },
            ));

            // ── Main card ────────────────────────────────────────────────
            root.spawn((
                Node {
                    width: Val::Px(PICKER_WIDTH + 64.0),
                    padding: UiRect::all(Val::Px(24.0)),
                    flex_direction: FlexDirection::Column,
                    row_gap: Val::Px(18.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, "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 {
                    flex_direction: FlexDirection::Row,
                    column_gap: Val::Px(16.0),
                    margin: UiRect::top(Val::Px(4.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),
            ));
        });
}

// ── 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_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 name_q: Query<&mut Text, With<NameValue>>,
    mut origin_q: Query<&mut Text, (With<OriginValue>, Without<NameValue>, Without<ShipValue>)>,
    mut ship_q: Query<&mut Text, (With<ShipValue>, Without<NameValue>, Without<OriginValue>)>,
    mut origin_blurb_q: Query<
        &mut Text,
        (
            With<OriginBlurb>,
            Without<NameValue>,
            Without<OriginValue>,
            Without<ShipValue>,
            Without<OriginBonus>,
            Without<ShipBlurb>,
        ),
    >,
    mut origin_bonus_q: Query<
        &mut Text,
        (
            With<OriginBonus>,
            Without<NameValue>,
            Without<OriginValue>,
            Without<ShipValue>,
            Without<OriginBlurb>,
            Without<ShipBlurb>,
        ),
    >,
    mut ship_blurb_q: Query<
        &mut Text,
        (
            With<ShipBlurb>,
            Without<NameValue>,
            Without<OriginValue>,
            Without<ShipValue>,
            Without<OriginBlurb>,
            Without<OriginBonus>,
        ),
    >,
) {
    // 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;
    }

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

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

    let ship = draft.ship();
    if let Ok(mut txt) = ship_q.single_mut() {
        let label = format!("{}  ·  {}", ship.name, ship.role);
        if txt.0 != label {
            txt.0 = label;
        }
    }
    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::Ship(delta) => draft.cycle_ship(delta),
        }
    }
}

/// Confirm / Back buttons. Confirm transitions to `AppState::InGame`; 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>,
    query: Query<(&Interaction, &CharacterCreationButton), Changed<Interaction>>,
) {
    for (interaction, button) in &query {
        if *interaction != Interaction::Pressed {
            continue;
        }
        match button {
            CharacterCreationButton::Confirm => {
                // TODO: persist `*draft` to SpacetimeDB before entering the game.
                bevy::log::info!(
                    "Confirming character: name={}, origin={}, ship={}",
                    draft.name(),
                    draft.origin().id,
                    draft.ship().id,
                );
                next_state.set(crate::state::AppState::InGame);
            }
            CharacterCreationButton::Back => {
                next_state.set(crate::state::AppState::Galaxy);
            }
        }
    }
}

// ── 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());
};
