shellquest 0.1.0

A passive RPG that lives in your terminal — your shell is the dungeon
use crate::character::{Character, Rarity};
use crate::journal::{EventType, JournalEntry};
use crate::zones::Zone;
use colored::*;

// ── Rich inline color helpers (MUD-style) ──

pub fn color_damage(n: i32) -> String { format!("{}", format!("{}", n).red().bold()) }
pub fn color_xp(n: u32) -> String { format!("+{} {}", format!("{}", n).cyan().bold(), "XP".cyan()) }
pub fn color_gold(n: u32) -> String { format!("+{} {}", format!("{}", n).yellow().bold(), "gold".yellow()) }
pub fn color_hp(hp: i32, max_hp: i32) -> String {
    let pct = hp as f32 / max_hp as f32;
    let hp_str = format!("{}/{}", hp, max_hp);
    if pct > 0.6 { format!("{}: {}", "HP".bold(), hp_str.green()) }
    else if pct > 0.3 { format!("{}: {}", "HP".bold(), hp_str.yellow()) }
    else { format!("{}: {}", "HP".bold(), hp_str.red().bold()) }
}
pub fn color_monster(name: &str) -> String { format!("{}", name.red().bold()) }
pub fn color_item_inline(name: &str, rarity: &Rarity) -> String {
    match rarity {
        Rarity::Common => format!("{}", name.white()),
        Rarity::Uncommon => format!("{}", name.dimmed().bold()),
        Rarity::Rare => format!("{}", name.green().bold()),
        Rarity::Epic => format!("{}{}{}", "".magenta(), name.magenta().bold(), "".magenta()),
        Rarity::Legendary => format!("{}{}{}", "".yellow().bold(), name.yellow().bold().on_black(), "".yellow().bold()),
    }
}
pub fn color_zone(name: &str, zone: &Zone) -> String {
    use crate::zones::ZoneColor;
    match zone.color {
        ZoneColor::Green => format!("{}", name.green().bold()),
        ZoneColor::Yellow => format!("{}", name.yellow().bold()),
        ZoneColor::Red => format!("{}", name.red().bold()),
        ZoneColor::Blue => format!("{}", name.blue().bold()),
        ZoneColor::Magenta => format!("{}", name.magenta().bold()),
        ZoneColor::Cyan => format!("{}", name.cyan().bold()),
    }
}

// ── Print functions (accept pre-colored or plain strings) ──

pub fn print_combat_win(msg: &str) {
    eprintln!("{} {}", "⚔️ ".bold(), msg);
}

pub fn print_combat_tough(msg: &str, died: bool) {
    if died {
        eprintln!("{} {}", "💀".bold(), msg);
    } else {
        eprintln!("{} {}", "⚔️ ".bold(), msg);
    }
}

pub fn print_combat_lose(msg: &str, died: bool) {
    if died {
        eprintln!("{} {}", "💀".bold(), msg);
    } else {
        eprintln!("{} {}", "🩸".bold(), msg);
    }
}

pub fn print_combat_draw(msg: &str) {
    eprintln!("{} {}", "👻".bold(), msg.dimmed());
}

pub fn print_trap(msg: &str) {
    eprintln!("{} {}", "🪤".bold(), msg);
}

pub fn print_travel(msg: &str, _zone: &Zone) {
    eprintln!("{} {}", "🗺️ ".bold(), msg);
}

pub fn print_craft(msg: &str) {
    eprintln!("{} {}", "🔨".bold(), msg);
}

pub fn print_quest(msg: &str) {
    eprintln!("{} {}", "🏆".bold(), msg);
}

pub fn print_discovery(msg: &str) {
    eprintln!("{} {}", "🔮".bold(), msg);
}

pub fn print_loot(msg: &str, rarity: &Rarity) {
    match rarity {
        Rarity::Common => {
            eprintln!("{} {}", "📦".bold(), msg.white());
        }
        Rarity::Uncommon => {
            eprintln!("{} {} {}", "📦".bold(), "~".dimmed(), msg.dimmed().bold());
        }
        Rarity::Rare => {
            eprintln!("{} {} {} {}", "📦".bold(), "~~".green().bold(), msg.green().bold(), "~~".green().bold());
        }
        Rarity::Epic => {
            eprintln!("{} {} {} {}", "💎".bold(), "★·.·".magenta(), msg.magenta().bold().italic(), "·.·★".magenta());
        }
        Rarity::Legendary => {
            eprintln!("{}", "╔═══════════════════════════════════════════╗".yellow().bold());
            eprintln!("{} {} {} {}", "".yellow().bold(), "✦✦✦".yellow().bold().on_black(), msg.yellow().bold().on_black(), "✦✦✦".yellow().bold().on_black());
            eprintln!("{}", "╚═══════════════════════════════════════════╝".yellow().bold());
        }
    }
}

fn format_item_rarity(name: &str, rarity: &Rarity) -> (String, String) {
    match rarity {
        Rarity::Common => (
            name.white().to_string(),
            "[Common]".dimmed().to_string(),
        ),
        Rarity::Uncommon => (
            name.dimmed().bold().to_string(),
            "[Uncommon]".dimmed().to_string(),
        ),
        Rarity::Rare => (
            format!("{}", name.green().bold()),
            format!("{}", "[Rare]".green().bold()),
        ),
        Rarity::Epic => (
            format!("{}{}{}", "".magenta(), name.magenta().bold().italic(), "".magenta()),
            format!("{}", "[Epic]".magenta().bold()),
        ),
        Rarity::Legendary => (
            format!("{}{}{}", "".yellow().bold(), name.yellow().bold().on_black(), "".yellow().bold()),
            format!("{}", "[LEGENDARY]".yellow().bold().on_black()),
        ),
    }
}

pub fn print_gold(msg: &str) {
    eprintln!("{} {}", "💰".bold(), msg);
}

pub fn print_familiar(msg: &str) {
    eprintln!("{} {}", "🐾".bold(), msg);
}

pub fn print_portal(msg: &str) {
    eprintln!("{} {}", "🌀".bold(), msg);
}

pub fn print_power(msg: &str) {
    eprintln!("{} {}", "".bold(), msg);
}

pub fn print_level_up(msg: &str) {
    eprintln!("{}", "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━".yellow().bold());
    eprintln!("{} {}", "🎉".bold(), msg);
    eprintln!("{}", "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━".yellow().bold());
}

pub fn print_status(char: &Character) {
    let class_colored = format!("{}", char.class).cyan().bold();
    let race_colored = format!("{}", char.race).magenta();

    println!();
    println!("{}", "┌──────────────────────────────────────────┐".dimmed());
    let subclass_str = char.subclass.as_ref().map_or(String::from(" "), |s| {
        format!(" {} ", format!("{}", s).magenta().bold())
    });
    let prestige_str = if char.prestige > 0 {
        format!(" [{}{}]", "P".yellow().bold(), format!("{}", char.prestige).yellow().bold())
    } else {
        String::new()
    };
    println!(
        "{}  {} {} {}{}{}  (Lvl {}{})",
        "".dimmed(),
        char.name.bold().white(),
        "the".dimmed(),
        race_colored,
        subclass_str,
        class_colored,
        format!("{}", char.level).yellow().bold(),
        prestige_str
    );
    println!("{}", "".dimmed());

    // HP bar
    let hp_pct = char.hp as f32 / char.max_hp as f32;
    let bar_len = 20;
    let filled = (hp_pct * bar_len as f32) as usize;
    let empty = bar_len - filled;
    let hp_color = if hp_pct > 0.6 {
        "green"
    } else if hp_pct > 0.3 {
        "yellow"
    } else {
        "red"
    };
    let bar = format!("{}{}",
        "".repeat(filled),
        "".repeat(empty)
    );
    let bar_colored = match hp_color {
        "green" => bar.green(),
        "yellow" => bar.yellow(),
        _ => bar.red(),
    };
    println!(
        "{}  {} {} {}/{}",
        "".dimmed(),
        "HP:".bold(),
        bar_colored,
        format!("{}", char.hp).bold(),
        char.max_hp
    );

    // XP bar
    let xp_pct = char.xp as f32 / char.xp_to_next as f32;
    let xp_filled = (xp_pct * bar_len as f32) as usize;
    let xp_empty = bar_len - xp_filled;
    let xp_bar = format!("{}{}",
        "".repeat(xp_filled),
        "".repeat(xp_empty)
    );
    println!(
        "{}  {} {} {}/{}",
        "".dimmed(),
        "XP:".bold(),
        xp_bar.cyan(),
        char.xp,
        char.xp_to_next
    );

    println!("{}", "".dimmed());
    println!(
        "{}  {} {}  {} {}  {} {}",
        "".dimmed(),
        "STR:".bold(),
        format!("{}", char.strength).red(),
        "DEX:".bold(),
        format!("{}", char.dexterity).green(),
        "INT:".bold(),
        format!("{}", char.intelligence).blue()
    );
    println!(
        "{}  {} {}",
        "".dimmed(),
        "Gold:".bold(),
        format!("{}", char.gold).yellow()
    );
    println!("{}", "".dimmed());

    // Equipment
    let weapon_str = char.weapon.as_ref().map_or("(none)".dimmed().to_string(), |w| {
        let (name, rarity) = format_item_rarity(&w.name, &w.rarity);
        format!("{} (+{}) {}", name, w.power, rarity)
    });
    let armor_str = char.armor.as_ref().map_or("(none)".dimmed().to_string(), |a| {
        let (name, rarity) = format_item_rarity(&a.name, &a.rarity);
        format!("{} (+{}) {}", name, a.power, rarity)
    });
    let ring_str = char.ring.as_ref().map_or("(none)".dimmed().to_string(), |r| {
        let (name, rarity) = format_item_rarity(&r.name, &r.rarity);
        format!("{} (+{}) {}", name, r.power, rarity)
    });

    println!("{}  {} {}", "".dimmed(), "Weapon:".bold(), weapon_str);
    println!("{}  {} {}", "".dimmed(), "Armor: ".bold(), armor_str);
    println!("{}  {} {}", "".dimmed(), "Ring:  ".bold(), ring_str);
    println!("{}", "".dimmed());

    println!(
        "{}  {} {}  {} {}  {} {}",
        "".dimmed(),
        "Kills:".bold(),
        format!("{}", char.kills).green(),
        "Deaths:".bold(),
        format!("{}", char.deaths).red(),
        "Cmds:".bold(),
        format!("{}", char.commands_run).cyan()
    );
    println!(
        "{}  {} {}",
        "".dimmed(),
        "Title:".bold(),
        char.title.yellow().italic()
    );
    println!("{}", "└──────────────────────────────────────────┘".dimmed());
    println!();
}

pub fn print_inventory(char: &Character) {
    println!();
    println!("{}", "📦 Inventory".bold().cyan());
    println!("{}", "".repeat(40).dimmed());

    if char.inventory.is_empty() {
        println!("{}", "  (empty)".dimmed());
    } else {
        for (i, item) in char.inventory.iter().enumerate() {
            let (name_styled, rarity_styled) = format_item_rarity(&item.name, &item.rarity);
            println!(
                "  {}. {} (+{} {}) {}",
                format!("{}", i + 1).dimmed(),
                name_styled,
                item.power,
                format!("{}", item.slot).dimmed(),
                rarity_styled
            );
        }
    }
    println!();
}

pub fn print_journal(entries: &[JournalEntry]) {
    println!();
    println!("{}", "📜 Adventure Journal".bold().yellow());
    println!("{}", "".repeat(50).dimmed());

    if entries.is_empty() {
        println!("{}", "  No entries yet. Go run some commands!".dimmed());
    } else {
        let recent: Vec<&JournalEntry> = entries.iter().rev().take(20).collect();
        for entry in recent.iter().rev() {
            let time = entry.timestamp.format("%m/%d %H:%M").to_string().dimmed();
            let icon = match entry.event_type {
                EventType::Combat => "⚔️ ",
                EventType::Loot => "📦",
                EventType::Travel => "🗺️ ",
                EventType::Discovery => "🔮",
                EventType::LevelUp => "🎉",
                EventType::Death => "💀",
                EventType::Quest => "🏆",
                EventType::Craft => "🔨",
            };
            let msg_colored = match entry.event_type {
                EventType::Combat => entry.message.white().to_string(),
                EventType::Loot => entry.message.green().to_string(),
                EventType::Travel => entry.message.cyan().to_string(),
                EventType::Discovery => entry.message.magenta().to_string(),
                EventType::LevelUp => entry.message.yellow().bold().to_string(),
                EventType::Death => entry.message.red().bold().to_string(),
                EventType::Quest => entry.message.yellow().to_string(),
                EventType::Craft => entry.message.cyan().to_string(),
            };
            println!("  {} {} {}", time, icon, msg_colored);
        }
    }
    println!();
}