use anyhow::Result;
use clap::Subcommand;
use rust_decimal::Decimal;
use std::str::FromStr;

use bpx_api_types::order::{ExecuteOrderPayload, OrderType, Side, TimeInForce};

use crate::client;
use crate::config::{self, ResolvedConfig, StrategyConfig};
use crate::output::strategy as out;
use crate::output::OutputFormat;

#[derive(Debug, Subcommand)]
pub enum StrategyCommand {
    /// List all saved strategies
    List,

    /// Create a new strategy template
    Create {
        /// Strategy name
        name: String,
        /// Market symbol (e.g. SOL_USDC)
        #[arg(long)]
        symbol: String,
        /// Order side [buy, sell]
        #[arg(long)]
        side: String,
        /// Order size
        #[arg(long)]
        size: String,
        /// Stop-loss trigger price
        #[arg(long)]
        stop_loss: Option<String>,
        /// Take-profit trigger price
        #[arg(long)]
        take_profit: Option<String>,
        /// Enable auto-lend
        #[arg(long)]
        auto_lend: bool,
    },

    /// Execute a saved strategy
    Run {
        /// Strategy name to run
        name: String,
        /// Preview the order without executing
        #[arg(long)]
        dry_run: bool,
        /// Multiply the strategy size (e.g. 2.0 for double)
        #[arg(long)]
        size_multiplier: Option<f64>,
    },

    /// Delete a saved strategy
    Delete {
        /// Strategy name to delete
        name: String,
    },
}

pub async fn run(
    cmd: &StrategyCommand,
    config: &ResolvedConfig,
    secret: Option<&str>,
    base_url: Option<&str>,
    format: OutputFormat,
) -> Result<()> {
    match cmd {
        StrategyCommand::List => {
            let strategies = config::load_strategies()?;
            out::print_strategies(&strategies, format);
        }

        StrategyCommand::Create {
            name,
            symbol,
            side,
            size,
            stop_loss,
            take_profit,
            auto_lend,
        } => {
            let side_lower = side.to_lowercase();
            if side_lower != "buy" && side_lower != "sell" {
                return Err(anyhow::anyhow!(
                    "Invalid side: '{side}'. Use 'buy' or 'sell'"
                ));
            }

            let strategy = StrategyConfig {
                symbol: symbol.to_uppercase(),
                side: side_lower,
                size: size.clone(),
                stop_loss: stop_loss.clone(),
                take_profit: take_profit.clone(),
                auto_lend: *auto_lend,
            };

            config::save_strategy(name, &strategy)?;
            out::print_strategy_created(name, &strategy, format);
        }

        StrategyCommand::Run {
            name,
            dry_run,
            size_multiplier,
        } => {
            let strategies = config::load_strategies()?;
            let strategy = strategies
                .get(name)
                .ok_or_else(|| anyhow::anyhow!("Strategy '{}' not found", name))?;

            let mut size = Decimal::from_str(&strategy.size)
                .map_err(|_| anyhow::anyhow!("Invalid size in strategy: '{}'", strategy.size))?;

            if let Some(mult) = size_multiplier {
                let mult_dec = Decimal::from_str(&mult.to_string())
                    .map_err(|_| anyhow::anyhow!("Invalid size multiplier: '{mult}'"))?;
                size *= mult_dec;
            }

            let side = match strategy.side.as_str() {
                "buy" => Side::Bid,
                "sell" => Side::Ask,
                other => return Err(anyhow::anyhow!("Invalid side in strategy: '{other}'")),
            };

            let order_type = OrderType::Market;

            let mut payload = ExecuteOrderPayload {
                symbol: strategy.symbol.clone(),
                side,
                order_type,
                quantity: Some(size),
                time_in_force: Some(TimeInForce::IOC),
                auto_lend: Some(strategy.auto_lend),
                ..Default::default()
            };

            if let Some(sl) = &strategy.stop_loss {
                let sl_price = Decimal::from_str(sl)
                    .map_err(|_| anyhow::anyhow!("Invalid stop-loss price: '{sl}'"))?;
                payload.stop_loss_trigger_price = Some(sl_price);
            }
            if let Some(tp) = &strategy.take_profit {
                let tp_price = Decimal::from_str(tp)
                    .map_err(|_| anyhow::anyhow!("Invalid take-profit price: '{tp}'"))?;
                payload.take_profit_trigger_price = Some(tp_price);
            }

            if *dry_run {
                out::print_strategy_dry_run(name, strategy, &payload, size, format);
                return Ok(());
            }

            let c = client::authenticated_client(config, secret, base_url)?;
            let sp = client::spinner_for("Executing strategy order...", format);
            let result = c.execute_order(payload).await;
            sp.finish_and_clear();
            let order = result.map_err(|e| anyhow::anyhow!("{e}"))?;
            out::print_strategy_executed(name, &order, format);
        }

        StrategyCommand::Delete { name } => {
            let removed = config::delete_strategy(name)?;
            if removed {
                out::print_strategy_deleted(name, format);
            } else {
                return Err(anyhow::anyhow!("Strategy '{}' not found", name));
            }
        }
    }

    Ok(())
}
