//! Static game content: the six dice faces, the die kinds and their face layouts,
//! the room themes (reikhelm's palette), and the Lineage × Calling asymmetry tables.
//!
//! v2 (post-critique): Lineages carry **behavioral** hooks, not just stat tweaks, and
//! the dead/redundant grid cells are fixed (see DESIGN.md Part C). The magnitudes that
//! the balance sweeps actually move live in [`crate::config::Config`]; the structural
//! asymmetry lives here.

use serde::{Deserialize, Serialize};

/// The six symbols a die can show.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum Face {
    Steel,
    Cunning,
    Faith,
    Greed,
    Mettle,
    Dread,
}

/// A tally of rolled symbols for one turn.
#[derive(Clone, Copy, Debug, Default)]
pub struct Symbols {
    pub steel: i32,
    pub cunning: i32,
    pub faith: i32,
    pub greed: i32,
    pub mettle: i32,
    pub dread: i32,
}

impl Symbols {
    pub fn add_face(&mut self, f: Face) {
        match f {
            Face::Steel => self.steel += 1,
            Face::Cunning => self.cunning += 1,
            Face::Faith => self.faith += 1,
            Face::Greed => self.greed += 1,
            Face::Mettle => self.mettle += 1,
            Face::Dread => self.dread += 1,
        }
    }
}

/// The kinds of die a bag can hold. Each has a fixed six-face layout.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum DieKind {
    Recruit,
    Warrior,
    Scholar,
    Priest,
    Rogue,
    Champion,
    Relic,
}

impl DieKind {
    /// The six faces of this die kind (index by a d6 roll).
    pub fn faces(self) -> [Face; 6] {
        use Face::*;
        match self {
            // Balanced starter: one of each.
            DieKind::Recruit => [Steel, Cunning, Faith, Greed, Mettle, Dread],
            DieKind::Warrior => [Steel, Steel, Steel, Greed, Mettle, Dread],
            DieKind::Scholar => [Cunning, Cunning, Faith, Mettle, Greed, Dread],
            DieKind::Priest => [Faith, Faith, Cunning, Mettle, Greed, Dread],
            DieKind::Rogue => [Greed, Greed, Cunning, Steel, Mettle, Dread],
            // Premium: no Dread.
            DieKind::Champion => [Steel, Steel, Cunning, Faith, Greed, Mettle],
            // Premium loot/wild engine die: no Dread, no martial faces.
            DieKind::Relic => [Greed, Greed, Greed, Faith, Mettle, Mettle],
        }
    }

    /// A crude "quality" used when choosing which die to shed on a Setback (lowest first).
    pub fn quality(self) -> i32 {
        match self {
            DieKind::Recruit => 0,
            DieKind::Warrior | DieKind::Scholar | DieKind::Priest | DieKind::Rogue => 1,
            DieKind::Champion => 2,
            DieKind::Relic => 2,
        }
    }
}

/// Room themes, lifted from `reikhelm-core`'s `RegionTheme`. Each carries the
/// board-game stats the design assigns it.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum Theme {
    Threshold,
    Stone,
    Cistern,
    Den,
    Crypt,
    Hall,
    Library,
    Forge,
    Vault,
    Throne,
}

/// Stats for a room theme: glory base, combat threat, mechanism lock, what it produces
/// while held, whether it demands the trinity, and whether it's a "combat" room
/// (Gnoll's scavenge bonus).
#[derive(Clone, Copy, Debug)]
pub struct ThemeStats {
    pub base_value: f64,
    pub threat: i32,
    pub lock: i32,
    pub produces: Option<Face>,
    pub needs_trinity: bool,
    pub combat_room: bool,
}

impl Theme {
    pub fn stats(self) -> ThemeStats {
        use Face::*;
        let s = |base_value, threat, lock, produces, needs_trinity, combat_room| ThemeStats {
            base_value,
            threat,
            lock,
            produces,
            needs_trinity,
            combat_room,
        };
        match self {
            Theme::Threshold => s(1.0, 0, 0, None, false, false),
            Theme::Stone => s(2.0, 1, 0, None, false, false),
            Theme::Cistern => s(3.0, 1, 1, Some(Mettle), false, false),
            Theme::Den => s(3.0, 3, 0, Some(Steel), false, true),
            Theme::Crypt => s(3.0, 2, 1, Some(Faith), false, true),
            Theme::Hall => s(4.0, 1, 1, Some(Mettle), false, false),
            Theme::Library => s(4.0, 1, 2, Some(Cunning), false, false),
            Theme::Forge => s(5.0, 4, 1, Some(Steel), false, true),
            // v2: Vault lock 3 -> 2 (was a ~5% one-turn lottery). Still trinity-gated.
            Theme::Vault => s(6.0, 2, 2, Some(Greed), true, false),
            Theme::Throne => s(7.0, 5, 2, Some(Greed), false, true),
        }
    }
}

/// Themes ordered from poorest to richest — the richness gradient used to weight a
/// floor's themes by depth.
pub const RICHNESS_ORDER: [Theme; 10] = [
    Theme::Threshold,
    Theme::Stone,
    Theme::Cistern,
    Theme::Den,
    Theme::Crypt,
    Theme::Hall,
    Theme::Library,
    Theme::Forge,
    Theme::Vault,
    Theme::Throne,
];

// ---------------------------------------------------------------------------------
// Lineages (the "race" half)
// ---------------------------------------------------------------------------------

#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum Lineage {
    Dvergar,
    Wisp,
    Revenant,
    Gnoll,
    Sylphid,
    GolemBorn,
}

pub const ALL_LINEAGES: [Lineage; 6] = [
    Lineage::Dvergar,
    Lineage::Wisp,
    Lineage::Revenant,
    Lineage::Gnoll,
    Lineage::Sylphid,
    Lineage::GolemBorn,
];

/// Resolved modifiers a Lineage contributes. Built once per party.
#[derive(Clone, Debug)]
pub struct LineageMods {
    pub move_mod: i32,
    pub hand_mod: i32,
    pub steel_mod: i32,
    pub cunning_mod: i32,
    pub faith_mod: i32,
    /// Dvergar: reduces a room's Steel threat when claiming (smash through). v2 made
    /// this a *reduction* rather than a full ignore — total ignore was a 78%-winrate
    /// blowout in the sweeps because it deleted one of the two claim gates outright.
    pub threat_reduction: i32,
    /// Revenant: convert leftover Dread into Mettle 1:1 (peril as fuel)...
    pub dread_to_mettle: bool,
    /// ...and Revenant cannot use Faith to cancel Dread (no living soul).
    pub can_cancel_dread: bool,
    /// Gnoll: bonus loot multiplier when claiming a combat room.
    pub combat_loot_bonus: f64,
    /// Golem-born: never loses dice to a Setback.
    pub setback_immune: bool,
    /// Wisp: a Setback sheds this many extra dice (fragile).
    pub setback_extra_loss: i32,
    pub starting_bag: Vec<DieKind>,
}

impl Lineage {
    pub fn name(self) -> &'static str {
        match self {
            Lineage::Dvergar => "Dvergar",
            Lineage::Wisp => "Wisp",
            Lineage::Revenant => "Revenant",
            Lineage::Gnoll => "Gnoll",
            Lineage::Sylphid => "Sylphid",
            Lineage::GolemBorn => "GolemBorn",
        }
    }

    pub fn mods(self) -> LineageMods {
        use DieKind::*;
        // Base template; each arm overrides what it changes.
        let base = LineageMods {
            move_mod: 0,
            hand_mod: 0,
            steel_mod: 0,
            cunning_mod: 0,
            faith_mod: 0,
            threat_reduction: 0,
            dread_to_mettle: false,
            can_cancel_dread: true,
            combat_loot_bonus: 0.0,
            setback_immune: false,
            setback_extra_loss: 0,
            starting_bag: vec![],
        };
        match self {
            // Smash-through bruiser: shrugs off 1 threat, but godless (poor at Dread).
            // v2 iter9: -1 Cunning (iter8) over-nerfed it — Cunning gates locks *and*
            // buys Moves, so Dvergar cratered to ~6%. The cost moved to -1 Faith: it
            // still claims locks fine, but courts Setbacks. A gentler, real trade-off.
            Lineage::Dvergar => LineageMods {
                faith_mod: -1,
                threat_reduction: 1,
                starting_bag: vec![Recruit, Recruit, Recruit, Warrior, Warrior, Warrior],
                ..base
            },
            // Nimble caster: draws +1 die every hand. v2 iter8: an all-Scholar bag left
            // it starved of Steel and unable to clear room threats (a 5–7% Lineage); a
            // lone Warrior gives it just enough muscle to claim, while the extra die and
            // its natural extra Dread stay the trade-off.
            Lineage::Wisp => LineageMods {
                hand_mod: 1,
                starting_bag: vec![Recruit, Recruit, Recruit, Scholar, Scholar, Warrior],
                ..base
            },
            // Fearless dead: turns Dread into Mettle, but can't cancel it with Faith.
            Lineage::Revenant => LineageMods {
                dread_to_mettle: true,
                can_cancel_dread: false,
                starting_bag: vec![Recruit, Recruit, Recruit, Recruit, Warrior, Priest],
                ..base
            },
            // Scavenger: rich loot from combat rooms, but godless (poor at quelling
            // Dread). v2 iter3: the cost moved off Cunning (which gated locks and tanked
            // the whole Lineage) onto Faith, so Gnoll claims fine but courts Setbacks.
            Lineage::Gnoll => LineageMods {
                faith_mod: -1,
                combat_loot_bonus: 0.6,
                starting_bag: vec![Recruit, Recruit, Recruit, Warrior, Warrior, Rogue],
                ..base
            },
            // Fleet and deft at locks, but reckless. v2: dropped the -1 Steel (it was a
            // ~1%-winrate death sentence — Steel is the primary claim gate); the cost is
            // now fragility, paid only when the dice betray you.
            Lineage::Sylphid => LineageMods {
                move_mod: 1,
                cunning_mod: 1,
                setback_extra_loss: 1,
                starting_bag: vec![Recruit, Recruit, Recruit, Scholar, Scholar, Rogue],
                ..base
            },
            // Immovable: setback-immune, but rigid (-1 hand). Big balanced bag.
            // The -1 hand is orthogonal to every Calling and *cancels* Prospector's
            // +1 hand rather than negating an identity (no dead cell).
            Lineage::GolemBorn => LineageMods {
                hand_mod: -1,
                setback_immune: true,
                starting_bag: vec![Recruit, Recruit, Recruit, Recruit, Recruit, Warrior, Warrior],
                ..base
            },
        }
    }
}

// ---------------------------------------------------------------------------------
// Callings (the "class/faction" half)
// ---------------------------------------------------------------------------------

#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum Calling {
    Reaver,
    Lorekeeper,
    Wardancer,
    Hierophant,
    Warden,
    Prospector,
}

pub const ALL_CALLINGS: [Calling; 6] = [
    Calling::Reaver,
    Calling::Lorekeeper,
    Calling::Wardancer,
    Calling::Hierophant,
    Calling::Warden,
    Calling::Prospector,
];

/// Resolved modifiers a Calling contributes.
#[derive(Clone, Debug)]
pub struct CallingMods {
    pub recruit_kind: DieKind,
    pub recruit_discount: i32,
    pub hand_mod: i32,
    pub move_mod: i32,
    /// Reaver: bonus loot multiplier on every claimed room.
    pub claim_loot_bonus: f64,
    /// Lorekeeper: open Vaults without the trinity, plus a lock bonus.
    pub solo_vault: bool,
    pub cunning_bonus: i32,
    /// Wardancer: Worthy-End multiplier, and a free Last Stand (no deep room needed).
    pub worthy_mult: f64,
    pub free_last_stand: bool,
    /// Flat bonus to every control marker's strength (a consistent edge so a Calling
    /// isn't hostage to a rare event). Distinct from Warden's `marker_mult`.
    pub claim_strength_bonus: i32,
    /// Warden: control-marker strength multiplier.
    pub marker_mult: i32,
    /// Hierophant: extra Dread cancelled, heals Setbacks, richer held-room production.
    pub extra_dread_cancel: i32,
    pub heal_setback: bool,
    pub hold_loot_mult: f64,
    /// Prospector: may recruit Relic dice.
    pub finds_relics: bool,
}

impl Calling {
    pub fn name(self) -> &'static str {
        match self {
            Calling::Reaver => "Reaver",
            Calling::Lorekeeper => "Lorekeeper",
            Calling::Wardancer => "Wardancer",
            Calling::Hierophant => "Hierophant",
            Calling::Warden => "Warden",
            Calling::Prospector => "Prospector",
        }
    }

    pub fn mods(self) -> CallingMods {
        let base = CallingMods {
            recruit_kind: DieKind::Recruit,
            recruit_discount: 0,
            hand_mod: 0,
            move_mod: 0,
            claim_loot_bonus: 0.0,
            solo_vault: false,
            cunning_bonus: 0,
            worthy_mult: 1.0,
            free_last_stand: false,
            claim_strength_bonus: 0,
            marker_mult: 1,
            extra_dread_cancel: 0,
            heal_setback: false,
            hold_loot_mult: 1.0,
            finds_relics: false,
        };
        match self {
            // v2 iter4/9: claim loot 0.75 -> 0.5 -> 0.4. Loot-on-claim is a flywheel
            // (every claim funds the next), so it stayed the strongest Calling across
            // Lineages even after the first trim.
            Calling::Reaver => CallingMods {
                recruit_kind: DieKind::Warrior,
                recruit_discount: 1,
                claim_loot_bonus: 0.4,
                ..base
            },
            // v2: dropped the +1 lock Cunning bonus — solo-Vault access to the richest
            // rooms is already its identity; stacking a general lock bonus on top made
            // it strong with *every* Lineage. Now it's good at Vaults, not all locks.
            Calling::Lorekeeper => CallingMods {
                recruit_kind: DieKind::Scholar,
                solo_vault: true,
                ..base
            },
            // v2 iter3: the worthy *specialist* was the worst Calling because anyone can
            // martyr on a deep Throne — the bonus was redundant. Now it's genuinely the
            // best at the martyr line: premium Champion dice (reach + hold + crack the
            // Vault it dies on) and a 1.8x payout that clearly beats a generic 1.0x End.
            // v2 iter7: added a consistent +1 marker strength. The sweeps proved that
            // for a glory-maximizing bot, Worthy End and Cash Out are *substitutes*, so a
            // purely worthy-conditional Calling is dead weight whenever banking pays more
            // (which the depth dividend made common). The flat claim bonus makes Wardancer
            // a solid area-control crew that *also* dies huge — viable either way.
            Calling::Wardancer => CallingMods {
                recruit_kind: DieKind::Champion,
                move_mod: 1,
                worthy_mult: 2.2,
                free_last_stand: true,
                claim_strength_bonus: 1,
                ..base
            },
            Calling::Hierophant => CallingMods {
                recruit_kind: DieKind::Priest,
                extra_dread_cancel: 1,
                heal_setback: true,
                hold_loot_mult: 1.25,
                ..base
            },
            Calling::Warden => CallingMods {
                recruit_kind: DieKind::Champion,
                marker_mult: 2,
                ..base
            },
            Calling::Prospector => CallingMods {
                recruit_kind: DieKind::Relic,
                recruit_discount: 1,
                hand_mod: 1,
                finds_relics: true,
                ..base
            },
        }
    }
}
