//! `fxt risk` -- risk management.

use anyhow::Result;
use clap::{Parser, Subcommand};
use colored::Colorize;
use serde::Serialize;

use crate::grpc::FoxhuntClient;
use crate::output::{self, HumanReadable, OutputFormat};

#[derive(Parser, Debug)]
pub struct RiskCommand {
    #[command(subcommand)]
    action: RiskAction,
}

#[derive(Subcommand, Debug)]
enum RiskAction {
    /// Show current risk status (VaR, exposure, limits)
    Status,
    /// Show/update risk limits
    Limits {
        /// Limit key to get/set (e.g. max_position_size)
        key: Option<String>,
        /// Value to set (omit to read current value)
        value: Option<String>,
    },
    /// Show drawdown analysis
    Drawdown,
    /// Run stress test scenarios
    Stress {
        /// Scenario name (e.g. flash-crash, vol-spike)
        #[arg(long)]
        scenario: Option<String>,
    },
    /// Emergency: kill switch
    Emergency {
        /// Confirm emergency shutdown
        #[arg(long)]
        confirm: bool,
    },
}

// ── Result types ──────────────────────────────────────────────────────

#[derive(Serialize)]
struct CircuitBreakerInfo {
    name: String,
    is_triggered: bool,
    trigger_reason: String,
    breaker_type: String,
}

#[derive(Serialize)]
struct RiskStatusResult {
    var_1d: f64,
    var_5d: f64,
    current_drawdown: f64,
    max_drawdown: f64,
    sharpe_ratio: f64,
    sortino_ratio: f64,
    volatility: f64,
    circuit_breakers: Vec<CircuitBreakerInfo>,
}

impl HumanReadable for RiskStatusResult {
    fn print_human(&self) {
        println!("{}", "Risk Status".cyan().bold());
        println!("  VaR (1-day):       {:.4}", self.var_1d);
        println!("  VaR (5-day):       {:.4}", self.var_5d);
        let dd_str = format!("{:.2}%", self.current_drawdown * 100.0);
        let dd_colored = if self.current_drawdown > 0.05 {
            dd_str.red().to_string()
        } else if self.current_drawdown > 0.02 {
            dd_str.yellow().to_string()
        } else {
            dd_str.green().to_string()
        };
        println!("  Current Drawdown:  {dd_colored}");
        println!("  Max Drawdown:      {:.2}%", self.max_drawdown * 100.0);
        println!("  Sharpe Ratio:      {:.2}", self.sharpe_ratio);
        println!("  Sortino Ratio:     {:.2}", self.sortino_ratio);
        println!("  Volatility:        {:.4}", self.volatility);

        println!();
        println!("{}", "Circuit Breakers".cyan().bold());
        if self.circuit_breakers.is_empty() {
            println!("  No circuit breakers configured.");
        }
        for cb in &self.circuit_breakers {
            let status = if cb.is_triggered {
                "TRIGGERED".red().bold().to_string()
            } else {
                "OK".green().to_string()
            };
            println!("  [{}] {} ({})", status, cb.name, cb.breaker_type);
            if cb.is_triggered && !cb.trigger_reason.is_empty() {
                println!("         Reason: {}", cb.trigger_reason);
            }
        }
    }
}

#[derive(Serialize)]
struct PositionRiskInfo {
    symbol: String,
    position_size: f64,
    market_value: f64,
    var_contribution: f64,
    concentration_risk: f64,
    liquidity_risk: f64,
}

#[derive(Serialize)]
struct LimitsResult {
    var_1d: f64,
    var_5d: f64,
    var_30d: f64,
    positions: Vec<PositionRiskInfo>,
}

impl HumanReadable for LimitsResult {
    fn print_human(&self) {
        println!("{}", "Position Limits".cyan().bold());
        println!("  Portfolio VaR (1d):  {:.4}", self.var_1d);
        println!("  Portfolio VaR (5d):  {:.4}", self.var_5d);
        println!("  Portfolio VaR (30d): {:.4}", self.var_30d);
        if !self.positions.is_empty() {
            println!();
            println!(
                "  {:>10}  {:>12}  {:>12}  {:>10}  {:>10}",
                "Symbol", "Size", "Market Val", "VaR Contr", "Concent."
            );
            for p in &self.positions {
                println!(
                    "  {:>10}  {:>12.2}  {:>12.2}  {:>10.4}  {:>9.1}%",
                    p.symbol,
                    p.position_size,
                    p.market_value,
                    p.var_contribution,
                    p.concentration_risk,
                );
            }
        }
    }
}

#[derive(Serialize)]
struct DrawdownResult {
    current_drawdown: f64,
    max_drawdown: f64,
    sharpe_ratio: f64,
    sortino_ratio: f64,
    beta: f64,
    alpha: f64,
}

impl HumanReadable for DrawdownResult {
    fn print_human(&self) {
        println!("{}", "Drawdown Analysis".cyan().bold());
        println!("  Current Drawdown:  {:.2}%", self.current_drawdown * 100.0);
        println!("  Max Drawdown:      {:.2}%", self.max_drawdown * 100.0);
        println!("  Sharpe Ratio:      {:.2}", self.sharpe_ratio);
        println!("  Sortino Ratio:     {:.2}", self.sortino_ratio);
        println!("  Beta:              {:.4}", self.beta);
        println!("  Alpha:             {:.4}", self.alpha);
    }
}

#[derive(Serialize)]
struct StressResult {
    symbol: String,
    portfolio_risk_score: f64,
    positions: Vec<PositionRiskInfo>,
}

impl HumanReadable for StressResult {
    fn print_human(&self) {
        println!("{}", "Stress Test Results".cyan().bold());
        if !self.symbol.is_empty() {
            println!("  Symbol filter: {}", self.symbol);
        }
        let score_str = format!("{:.1}", self.portfolio_risk_score);
        let score_colored = if self.portfolio_risk_score > 75.0 {
            score_str.red().to_string()
        } else if self.portfolio_risk_score > 50.0 {
            score_str.yellow().to_string()
        } else {
            score_str.green().to_string()
        };
        println!("  Portfolio Risk Score: {score_colored}/100");
        for p in &self.positions {
            println!();
            println!("  {} :", p.symbol.bold());
            println!("    Position Size:      {:.2}", p.position_size);
            println!("    Market Value:       {:.2}", p.market_value);
            println!("    VaR Contribution:   {:.4}", p.var_contribution);
            println!("    Concentration Risk: {:.1}", p.concentration_risk);
            println!("    Liquidity Risk:     {:.1}", p.liquidity_risk);
        }
    }
}

#[derive(Serialize)]
struct EmergencyResult {
    success: bool,
    message: String,
    affected_orders: Vec<String>,
}

impl HumanReadable for EmergencyResult {
    fn print_human(&self) {
        if self.success {
            println!(
                "{} {}",
                "EMERGENCY STOP ACTIVATED".red().bold(),
                self.message,
            );
            if !self.affected_orders.is_empty() {
                println!(
                    "  Affected orders ({}): {}",
                    self.affected_orders.len(),
                    self.affected_orders.join(", "),
                );
            }
        } else {
            println!("{}: {}", "Emergency stop failed".red(), self.message);
        }
    }
}

// ── Helpers ───────────────────────────────────────────────────────────

fn breaker_type_name(value: i32) -> &'static str {
    use crate::proto::risk::CircuitBreakerType;
    match CircuitBreakerType::try_from(value) {
        Ok(CircuitBreakerType::PortfolioLoss) => "portfolio-loss",
        Ok(CircuitBreakerType::SymbolVolatility) => "symbol-volatility",
        Ok(CircuitBreakerType::PositionSize) => "position-size",
        Ok(CircuitBreakerType::Drawdown) => "drawdown",
        _ => "unknown",
    }
}

fn position_risk_to_info(pr: &crate::proto::risk::PositionRisk) -> PositionRiskInfo {
    PositionRiskInfo {
        symbol: pr.symbol.clone(),
        position_size: pr.position_size,
        market_value: pr.market_value,
        var_contribution: pr.var_contribution,
        concentration_risk: pr.concentration_risk,
        liquidity_risk: pr.liquidity_risk,
    }
}

// ── Execute ───────────────────────────────────────────────────────────

impl RiskCommand {
    pub async fn execute(&self, client: &FoxhuntClient, format: OutputFormat) -> Result<()> {
        match &self.action {
            RiskAction::Status => {
                let metrics_resp = client
                    .risk()
                    .get_risk_metrics(crate::proto::risk::GetRiskMetricsRequest {
                        portfolio_id: None,
                    })
                    .await?
                    .into_inner();

                let cb_resp = client
                    .risk()
                    .get_circuit_breaker_status(
                        crate::proto::risk::GetCircuitBreakerStatusRequest { symbol: None },
                    )
                    .await?
                    .into_inner();

                let m = metrics_resp.metrics.unwrap_or_default();

                let circuit_breakers: Vec<CircuitBreakerInfo> = cb_resp
                    .circuit_breakers
                    .iter()
                    .map(|cb| CircuitBreakerInfo {
                        name: cb.name.clone(),
                        is_triggered: cb.is_triggered,
                        trigger_reason: cb.trigger_reason.clone().unwrap_or_default(),
                        breaker_type: breaker_type_name(cb.breaker_type).to_owned(),
                    })
                    .collect();

                let result = RiskStatusResult {
                    var_1d: m.portfolio_var_1d,
                    var_5d: m.portfolio_var_5d,
                    current_drawdown: m.current_drawdown,
                    max_drawdown: m.max_drawdown,
                    sharpe_ratio: m.sharpe_ratio,
                    sortino_ratio: m.sortino_ratio,
                    volatility: m.volatility,
                    circuit_breakers,
                };
                output::render(&result, format)?;
            }

            RiskAction::Limits { key: _, value: _ } => {
                let metrics_resp = client
                    .risk()
                    .get_risk_metrics(crate::proto::risk::GetRiskMetricsRequest {
                        portfolio_id: None,
                    })
                    .await?
                    .into_inner();

                let m = metrics_resp.metrics.unwrap_or_default();

                let positions: Vec<PositionRiskInfo> =
                    m.position_risks.iter().map(position_risk_to_info).collect();

                let result = LimitsResult {
                    var_1d: m.portfolio_var_1d,
                    var_5d: m.portfolio_var_5d,
                    var_30d: m.portfolio_var_30d,
                    positions,
                };
                output::render(&result, format)?;
            }

            RiskAction::Drawdown => {
                let metrics_resp = client
                    .risk()
                    .get_risk_metrics(crate::proto::risk::GetRiskMetricsRequest {
                        portfolio_id: None,
                    })
                    .await?
                    .into_inner();

                let m = metrics_resp.metrics.unwrap_or_default();

                let result = DrawdownResult {
                    current_drawdown: m.current_drawdown,
                    max_drawdown: m.max_drawdown,
                    sharpe_ratio: m.sharpe_ratio,
                    sortino_ratio: m.sortino_ratio,
                    beta: m.beta,
                    alpha: m.alpha,
                };
                output::render(&result, format)?;
            }

            RiskAction::Stress { scenario } => {
                let symbol_filter = scenario.clone().unwrap_or_default();

                let resp = client
                    .risk()
                    .get_position_risk(crate::proto::risk::GetPositionRiskRequest {
                        symbol: if symbol_filter.is_empty() {
                            None
                        } else {
                            Some(symbol_filter.clone())
                        },
                        account_id: None,
                    })
                    .await?
                    .into_inner();

                let positions: Vec<PositionRiskInfo> = resp
                    .position_risks
                    .iter()
                    .map(position_risk_to_info)
                    .collect();

                let result = StressResult {
                    symbol: symbol_filter,
                    portfolio_risk_score: resp.portfolio_risk_score,
                    positions,
                };
                output::render(&result, format)?;
            }

            RiskAction::Emergency { confirm } => {
                if !confirm {
                    println!(
                        "{}",
                        "Emergency stop requires --confirm flag.  This will halt ALL trading."
                            .yellow()
                    );
                    println!(
                        "  Usage: {} risk emergency --confirm",
                        "fxt".bold()
                    );
                    return Ok(());
                }

                let resp = client
                    .risk()
                    .emergency_stop(crate::proto::risk::EmergencyStopRequest {
                        stop_type: crate::proto::risk::EmergencyStopType::AllTrading as i32,
                        reason: "Manual emergency halt via fxt CLI".to_owned(),
                        symbol: None,
                        account_id: None,
                    })
                    .await?
                    .into_inner();

                let result = EmergencyResult {
                    success: resp.success,
                    message: resp.message,
                    affected_orders: resp.affected_orders,
                };
                output::render(&result, format)?;
            }
        }
        Ok(())
    }
}
