use anyhow::Result;
use clap::Subcommand;

use bpx_api_types::fill::{FillType, FillsHistoryParams};
use bpx_api_types::history::SortDirection;

use crate::client;
use crate::config::ResolvedConfig;
use crate::output::fills as out;
use crate::output::OutputFormat;

#[derive(Debug, Subcommand)]
pub enum FillsCommand {
    /// List historical fills
    List {
        /// Filter by symbol
        #[arg(long)]
        symbol: Option<String>,
        /// Start time (unix timestamp in milliseconds)
        #[arg(long)]
        from: Option<i64>,
        /// End time (unix timestamp in milliseconds)
        #[arg(long)]
        to: Option<i64>,
        /// Maximum number of results
        #[arg(long)]
        limit: Option<u64>,
        /// Offset for pagination
        #[arg(long)]
        offset: Option<u64>,
        /// Filter by order ID
        #[arg(long)]
        order_id: Option<String>,
        /// Filter by fill type [User, BookLiquidation, Adl, Backstop, Liquidation, AllLiquidation, CollateralConversion, CollateralConversionAndSpotLiquidation]
        #[arg(long)]
        fill_type: Option<String>,
        /// Sort direction [Asc, Desc]
        #[arg(long)]
        sort: Option<String>,
    },

    /// Trade analytics summary (volume, fees, PnL by symbol)
    Summary {
        /// Filter by symbol
        #[arg(long)]
        symbol: Option<String>,
        /// Start time (unix timestamp in milliseconds)
        #[arg(long)]
        from: Option<i64>,
        /// End time (unix timestamp in milliseconds)
        #[arg(long)]
        to: Option<i64>,
        /// Maximum fills to analyze
        #[arg(long, default_value = "1000")]
        limit: u64,
    },

    /// Extended analytics: VWAP, win rate, Sharpe, profit factor
    Analytics {
        /// Filter by symbol
        #[arg(long)]
        symbol: Option<String>,
        /// Time period (e.g. "7d", "30d", "24h")
        #[arg(long, default_value = "7d")]
        period: String,
        /// Maximum fills to analyze
        #[arg(long, default_value = "1000")]
        limit: u64,
    },
}

/// Parse a period string like "7d", "30d", "24h", "1h" into a `from` timestamp in milliseconds.
fn parse_period(period: &str) -> Result<i64> {
    let now_ms = chrono::Utc::now().timestamp_millis();
    let s = period.trim();
    let (num_str, unit) = s.split_at(s.len().saturating_sub(1));
    let num: i64 = num_str
        .parse()
        .map_err(|_| anyhow::anyhow!("Invalid period: '{period}'. Use e.g. 7d, 30d, 24h, 1h"))?;
    let ms = match unit {
        "d" => num * 24 * 60 * 60 * 1000,
        "h" => num * 60 * 60 * 1000,
        "m" => num * 60 * 1000,
        _ => {
            return Err(anyhow::anyhow!(
                "Invalid period unit: '{unit}'. Use d (days), h (hours), or m (minutes)"
            ))
        }
    };
    Ok(now_ms - ms)
}

pub async fn run(
    cmd: &FillsCommand,
    config: &ResolvedConfig,
    secret: Option<&str>,
    base_url: Option<&str>,
    format: OutputFormat,
) -> Result<()> {
    let c = client::authenticated_client(config, secret, base_url)?;

    match cmd {
        FillsCommand::List {
            symbol,
            from,
            to,
            limit,
            offset,
            order_id,
            fill_type,
            sort,
        } => {
            let mut params = FillsHistoryParams::default();
            if let Some(s) = symbol {
                params = params.with_symbol(s.to_uppercase());
            }
            if let Some(f) = from {
                params = params.with_from(*f);
            }
            if let Some(t) = to {
                params = params.with_to(*t);
            }
            if let Some(l) = limit {
                params = params.with_limit(*l);
            }
            if let Some(o) = offset {
                params = params.with_offset(*o);
            }
            if let Some(oid) = order_id {
                params = params.with_order_id(oid.clone());
            }
            if let Some(ft) = fill_type {
                let ft_enum = ft.parse::<FillType>().map_err(|_| {
                    anyhow::anyhow!("Invalid fill type: '{ft}'. Use User, BookLiquidation, Adl, Backstop, Liquidation, AllLiquidation, CollateralConversion, or CollateralConversionAndSpotLiquidation")
                })?;
                params = params.with_fill_type(ft_enum);
            }
            if let Some(s) = sort {
                let dir = s.parse::<SortDirection>().map_err(|_| {
                    anyhow::anyhow!("Invalid sort direction: '{s}'. Use Asc or Desc")
                })?;
                params = params.with_sort_direction(dir);
            }

            let sp = client::spinner_for("Fetching fills...", format);
            let result = c.get_historical_fills(params).await;
            sp.finish_and_clear();
            let fills = result.map_err(|e| anyhow::anyhow!("{e}"))?;
            out::print_fills(&fills, format);
        }

        FillsCommand::Summary {
            symbol,
            from,
            to,
            limit,
        } => {
            let mut params = FillsHistoryParams::default();
            if let Some(s) = symbol {
                params = params.with_symbol(s.to_uppercase());
            }
            if let Some(f) = from {
                params = params.with_from(*f);
            }
            if let Some(t) = to {
                params = params.with_to(*t);
            }
            params = params.with_limit(*limit);

            let sp = client::spinner_for("Fetching fills for summary...", format);
            let result = c.get_historical_fills(params).await;
            sp.finish_and_clear();
            let fills = result.map_err(|e| anyhow::anyhow!("{e}"))?;
            out::print_summary(&fills, format);
        }

        FillsCommand::Analytics {
            symbol,
            period,
            limit,
        } => {
            let from_ts = parse_period(period)?;
            let now_ms = chrono::Utc::now().timestamp_millis();

            let mut params = FillsHistoryParams::default();
            if let Some(s) = symbol {
                params = params.with_symbol(s.to_uppercase());
            }
            params = params.with_from(from_ts);
            params = params.with_to(now_ms);
            params = params.with_limit(*limit);

            let sp = client::spinner_for("Fetching fills for analytics...", format);
            let result = c.get_historical_fills(params).await;
            sp.finish_and_clear();
            let fills = result.map_err(|e| anyhow::anyhow!("{e}"))?;
            out::print_analytics(&fills, format);
        }
    }

    Ok(())
}
