//! Static presets and the editable draft resource for character creation.
//!
//! The draft is held in a [`CharacterDraft`] resource so the UI is fully
//! data-driven: panels read from the resource every frame, +/- buttons mutate
//! it, and the confirm handler can hand it off to whatever persistence layer
//! comes next (SpacetimeDB). No state lives on UI entities.
//!
//! Everything in this file is plain data — no Bevy types beyond `Resource`.

use bevy::prelude::*;

// ── Name pool ───────────────────────────────────────────────────────────────

/// Pool of preset character names. The UI cycles through these with prev/next
/// buttons. Eventually this becomes a free-text input once a text-input widget
/// lands (Bevy 0.16 has no built-in single-line text input).
pub const CHARACTER_NAMES: &[&str] = &[
    "Vera Solenne",
    "Kael Drennan",
    "Idris Maro",
    "Sloana Vex",
    "Mira Cathan",
    "Jove Rask",
    "Nyssa Korbel",
    "Tal Halberd",
    "Rin Okafor",
    "Des Cordova",
];

// ── Origins ─────────────────────────────────────────────────────────────────

/// Background / origin archetype. Each entry pairs flavor text with the
/// mechanical knobs that will eventually feed into starting stats and
/// standing. Pure data — applied at game-start time (not yet wired up).
#[derive(Debug, Clone, Copy)]
pub struct Origin {
    pub id: &'static str,
    pub name: &'static str,
    pub blurb: &'static str,
    /// One-line mechanical summary shown in the picker.
    pub bonus: &'static str,
}

pub const ORIGINS: &[Origin] = &[
    Origin {
        id: "navy",
        name: "Navy Veteran",
        blurb: "Years in a Concord patrol fleet taught you how to read a \
                battlefield and keep a ship together under fire. You left with \
                a modest pension, a contacts book full of quartermasters, and \
                a slightly exaggerated sense of discipline.",
        bonus: "+15% shield recharge   +10% weapon damage   starts at +2.0 Concord standing",
    },
    Origin {
        id: "hauler",
        name: "Freelance Hauler",
        blurb: "You have moved everything from livestock to liquid hydrogen \
                along the inner trade lanes. Your ship is your home, your \
                ledgers are immaculate, and you can quote the salvage code of \
                every border station from memory.",
        bonus: "+25% cargo capacity   +10% warp velocity   starts with a free trade permit",
    },
    Origin {
        id: "explorer",
        name: "Frontier Explorer",
        blurb: "Charted wormholes, mapped forgotten ruins, surveyed gas \
                giants nobody had named. Your logs are worth a fortune to the \
                right cartographer — and to the wrong faction.",
        bonus: "+30% scan range   +15% anomaly yield   starts with advanced survey drone",
    },
    Origin {
        id: "outcast",
        name: "Outer-Rim Outcast",
        blurb: "Born past the gate network where the only law is the cargo \
                you can defend. You learned to fly before you could read, and \
                you have the arrest record (and the enemies) to prove it.",
        bonus: "+10% sub-warp speed   +20% black-market prices   starts at −2.0 Concord standing",
    },
];

// ── Starting ships ──────────────────────────────────────────────────────────

/// Starting ship archetype. The picked hull seeds the player's initial entity
/// loadout once `AppState::InGame` is wired up. Flavor text only for now.
#[derive(Debug, Clone, Copy)]
pub struct StartingShip {
    pub id: &'static str,
    pub name: &'static str,
    pub role: &'static str,
    pub blurb: &'static str,
}

pub const STARTING_SHIPS: &[StartingShip] = &[
    StartingShip {
        id: "frigate",
        name: "Corsair-Class Frigate",
        role: "Frigate · Combat",
        blurb: "A nimble gunship: two turret hardpoints, reinforced plating, \
                and just enough hold for prize money. The cheap answer when \
                someone hails you with bad intentions.",
    },
    StartingShip {
        id: "hauler",
        name: "Mule-Class Transport",
        role: "Hauler · Trade",
        blurb: "Slow, fat, and a profit engine with a heartbeat. The hull \
                tanks anything that doesn't catch you, and the cargo bay \
                fits a small fortune if you keep moving.",
    },
    StartingShip {
        id: "scout",
        name: "Whisper-Class Scout",
        role: "Scout · Exploration",
        blurb: "Glass jaw, sharp eyes. Signature-cleared hull, long-range \
                sensors, and a probe launcher. Lives or dies on whether you \
                saw them first.",
    },
];

// ── Bannerlord-style backstory ─────────────────────────────────────────────

pub const BACKSTORY_QUESTION_COUNT: usize = 5;

#[derive(Debug, Clone, Copy, Default, Eq, PartialEq)]
pub struct CharacterStats {
    pub command: i32,
    pub piloting: i32,
    pub gunnery: i32,
    pub engineering: i32,
    pub trade: i32,
    pub scouting: i32,
    pub diplomacy: i32,
    pub grit: i32,
}

impl CharacterStats {
    pub const fn new(
        command: i32,
        piloting: i32,
        gunnery: i32,
        engineering: i32,
        trade: i32,
        scouting: i32,
        diplomacy: i32,
        grit: i32,
    ) -> Self {
        Self {
            command,
            piloting,
            gunnery,
            engineering,
            trade,
            scouting,
            diplomacy,
            grit,
        }
    }

    pub fn add(self, other: Self) -> Self {
        Self {
            command: self.command + other.command,
            piloting: self.piloting + other.piloting,
            gunnery: self.gunnery + other.gunnery,
            engineering: self.engineering + other.engineering,
            trade: self.trade + other.trade,
            scouting: self.scouting + other.scouting,
            diplomacy: self.diplomacy + other.diplomacy,
            grit: self.grit + other.grit,
        }
    }

    pub fn summary(self) -> String {
        format!(
            "Command {}   Piloting {}   Gunnery {}   Engineering {}   Trade {}   Scouting {}   Diplomacy {}   Grit {}",
            self.command,
            self.piloting,
            self.gunnery,
            self.engineering,
            self.trade,
            self.scouting,
            self.diplomacy,
            self.grit,
        )
    }
}

#[derive(Debug, Clone, Copy)]
pub struct BackstoryChoice {
    pub id: &'static str,
    pub name: &'static str,
    pub blurb: &'static str,
    pub stats: CharacterStats,
}

#[derive(Debug, Clone, Copy)]
pub struct BackstoryQuestion {
    pub prompt: &'static str,
    pub choices: &'static [BackstoryChoice],
}

pub const CHILDHOOD_CHOICES: &[BackstoryChoice] = &[
    BackstoryChoice {
        id: "dockborn",
        name: "Raised on station docks",
        blurb:
            "You grew up under gantries, learning ships by sound before you ever touched a helm.",
        stats: CharacterStats::new(0, 1, 0, 2, 1, 0, 0, 0),
    },
    BackstoryChoice {
        id: "merchant_house",
        name: "Merchant house ward",
        blurb: "Contract law, polite threats, and price sheets were dinner-table conversation.",
        stats: CharacterStats::new(0, 0, 0, 0, 3, 0, 1, 0),
    },
    BackstoryChoice {
        id: "frontier_clan",
        name: "Frontier clan child",
        blurb:
            "Your first lessons were vacuum discipline, ration math, and how to spot trouble early.",
        stats: CharacterStats::new(0, 1, 0, 0, 0, 2, 0, 1),
    },
];

pub const APPRENTICESHIP_CHOICES: &[BackstoryChoice] = &[
    BackstoryChoice {
        id: "engine_rat",
        name: "Engine-room apprentice",
        blurb: "You patched coolant lines with your sleeves rolled up and alarms howling.",
        stats: CharacterStats::new(0, 0, 0, 3, 0, 0, 0, 1),
    },
    BackstoryChoice {
        id: "deck_cadet",
        name: "Deck cadet",
        blurb: "You drilled formation flying, boarding defense, and the art of sounding calm.",
        stats: CharacterStats::new(2, 1, 1, 0, 0, 0, 0, 0),
    },
    BackstoryChoice {
        id: "route_scout",
        name: "Route scout",
        blurb: "You ran courier paths ahead of the main convoy with only sensors for company.",
        stats: CharacterStats::new(0, 2, 0, 0, 0, 2, 0, 0),
    },
];

pub const FIRST_COMMAND_CHOICES: &[BackstoryChoice] = &[
    BackstoryChoice {
        id: "convoy_guard",
        name: "Convoy guard",
        blurb: "Your first command kept traders alive through pirate weather and bad intel.",
        stats: CharacterStats::new(1, 0, 2, 0, 1, 0, 0, 0),
    },
    BackstoryChoice {
        id: "salvage_skipper",
        name: "Salvage skipper",
        blurb: "You made a living from broken hulls, cold wrecks, and systems nobody trusted.",
        stats: CharacterStats::new(0, 1, 0, 2, 0, 1, 0, 0),
    },
    BackstoryChoice {
        id: "free_trader",
        name: "Free trader",
        blurb: "You learned that a clean manifest is useful, and a flexible one is profitable.",
        stats: CharacterStats::new(0, 0, 0, 0, 3, 0, 1, 0),
    },
];

pub const CRISIS_CHOICES: &[BackstoryChoice] = &[
    BackstoryChoice {
        id: "mutiny_mediator",
        name: "Settled a mutiny",
        blurb: "You talked an armed crew down before anyone vented the wrong compartment.",
        stats: CharacterStats::new(1, 0, 0, 0, 0, 0, 3, 0),
    },
    BackstoryChoice {
        id: "ambush_survivor",
        name: "Survived an ambush",
        blurb: "You limped home on backup thrusters and spite, with half your armor gone.",
        stats: CharacterStats::new(0, 1, 2, 0, 0, 0, 0, 1),
    },
    BackstoryChoice {
        id: "blackout_fix",
        name: "Restored a dark station",
        blurb: "When the lights died, you brought power back one breaker at a time.",
        stats: CharacterStats::new(0, 0, 0, 3, 0, 0, 0, 1),
    },
];

pub const REASON_CHOICES: &[BackstoryChoice] = &[
    BackstoryChoice {
        id: "debt",
        name: "A debt to settle",
        blurb: "Someone owns a piece of your future. You intend to buy it back.",
        stats: CharacterStats::new(0, 0, 0, 0, 2, 0, 0, 2),
    },
    BackstoryChoice {
        id: "lost_signal",
        name: "A lost signal",
        blurb: "A voice from the dark keeps appearing in old survey bands. You need answers.",
        stats: CharacterStats::new(0, 1, 0, 0, 0, 3, 0, 0),
    },
    BackstoryChoice {
        id: "new_banner",
        name: "A banner of your own",
        blurb: "You are done serving other captains. The next legend wears your colors.",
        stats: CharacterStats::new(3, 0, 1, 0, 0, 0, 0, 0),
    },
];

pub const BACKSTORY_QUESTIONS: &[BackstoryQuestion; BACKSTORY_QUESTION_COUNT] = &[
    BackstoryQuestion {
        prompt: "You were born...",
        choices: CHILDHOOD_CHOICES,
    },
    BackstoryQuestion {
        prompt: "As a young adult, you trained as...",
        choices: APPRENTICESHIP_CHOICES,
    },
    BackstoryQuestion {
        prompt: "Your first command was...",
        choices: FIRST_COMMAND_CHOICES,
    },
    BackstoryQuestion {
        prompt: "You became known when you...",
        choices: CRISIS_CHOICES,
    },
    BackstoryQuestion {
        prompt: "You set out because of...",
        choices: REASON_CHOICES,
    },
];

// ── Draft resource ──────────────────────────────────────────────────────────

/// Editable character draft, mutated by the picker UI and read at confirm time.
///
/// Indices are stored rather than the strings/structs themselves so cycling is
/// trivial and the resource stays `Copy`. Panels resolve the current selection
/// via [`CharacterDraft::name`] / [`CharacterDraft::origin`] /
/// [`CharacterDraft::ship`].
#[derive(Resource, Debug, Clone, Copy, Default)]
pub struct CharacterDraft {
    pub name_index: usize,
    pub origin_index: usize,
    pub ship_index: usize,
    pub backstory_indices: [usize; BACKSTORY_QUESTION_COUNT],
}

impl CharacterDraft {
    pub fn name(&self) -> &'static str {
        CHARACTER_NAMES[self.name_index % CHARACTER_NAMES.len()]
    }

    pub fn origin(&self) -> &'static Origin {
        &ORIGINS[self.origin_index % ORIGINS.len()]
    }

    pub fn ship(&self) -> &'static StartingShip {
        &STARTING_SHIPS[self.ship_index % STARTING_SHIPS.len()]
    }

    pub fn backstory_choice(&self, question_index: usize) -> &'static BackstoryChoice {
        let question = &BACKSTORY_QUESTIONS[question_index % BACKSTORY_QUESTIONS.len()];
        let choice_index = self.backstory_indices[question_index] % question.choices.len();
        &question.choices[choice_index]
    }

    pub fn stats(&self) -> CharacterStats {
        let mut stats = CharacterStats::default();
        for question_index in 0..BACKSTORY_QUESTION_COUNT {
            stats = stats.add(self.backstory_choice(question_index).stats);
        }
        stats
    }

    /// Advance `field` by `delta`, wrapping around the length of `options`.
    /// Generic over the picker so the same code handles name / origin / ship.
    pub fn cycle(field: usize, delta: i32, options: usize) -> usize {
        debug_assert!(options > 0);
        let signed = field as i32 + delta;
        let modulo = signed.rem_euclid(options as i32);
        modulo as usize
    }

    pub fn cycle_name(&mut self, delta: i32) {
        self.name_index = Self::cycle(self.name_index, delta, CHARACTER_NAMES.len());
    }

    pub fn cycle_origin(&mut self, delta: i32) {
        self.origin_index = Self::cycle(self.origin_index, delta, ORIGINS.len());
    }

    pub fn cycle_ship(&mut self, delta: i32) {
        self.ship_index = Self::cycle(self.ship_index, delta, STARTING_SHIPS.len());
    }

    pub fn cycle_backstory(&mut self, question_index: usize, delta: i32) {
        let question = &BACKSTORY_QUESTIONS[question_index % BACKSTORY_QUESTIONS.len()];
        self.backstory_indices[question_index] = Self::cycle(
            self.backstory_indices[question_index],
            delta,
            question.choices.len(),
        );
    }
}

// ── Tests ───────────────────────────────────────────────────────────────────

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn name_index_wraps_forward() {
        let mut d = CharacterDraft {
            name_index: CHARACTER_NAMES.len() - 1,
            ..Default::default()
        };
        d.cycle_name(1);
        assert_eq!(d.name_index, 0);
    }

    #[test]
    fn name_index_wraps_backward() {
        let mut d = CharacterDraft {
            name_index: 0,
            ..Default::default()
        };
        d.cycle_name(-1);
        assert_eq!(d.name_index, CHARACTER_NAMES.len() - 1);
    }

    #[test]
    fn origin_index_advances_within_bounds() {
        let mut d = CharacterDraft::default();
        d.cycle_origin(1);
        assert_eq!(d.origin_index, 1);
        assert_eq!(d.origin().id, ORIGINS[1].id);
    }

    #[test]
    fn cycle_handles_large_negative() {
        // -100 % 4 should land on a valid index, never panic.
        let i = CharacterDraft::cycle(0, -100, ORIGINS.len());
        assert!(i < ORIGINS.len());
    }

    #[test]
    fn draft_resolves_current_selections() {
        let d = CharacterDraft {
            name_index: 2,
            origin_index: 1,
            ship_index: 0,
            backstory_indices: [0; BACKSTORY_QUESTION_COUNT],
        };
        assert_eq!(d.name(), CHARACTER_NAMES[2]);
        assert_eq!(d.origin().id, ORIGINS[1].id);
        assert_eq!(d.ship().id, STARTING_SHIPS[0].id);
    }

    #[test]
    fn backstory_choices_wrap_and_grant_stats() {
        let mut d = CharacterDraft::default();
        d.cycle_backstory(0, -1);
        assert_eq!(d.backstory_choice(0).id, "frontier_clan");
        assert_eq!(d.stats().scouting, 2);
        assert_eq!(d.stats().grit, 4);
    }
}
