//! `fxt data` -- data pipeline 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 DataCommand {
    #[command(subcommand)]
    action: DataAction,
}

#[derive(Subcommand, Debug)]
enum DataAction {
    /// Download market data
    Download {
        /// Symbol to download
        #[arg(long)]
        symbol: String,
        /// Start date (YYYY-MM-DD)
        #[arg(long)]
        from: String,
        /// End date (YYYY-MM-DD)
        #[arg(long)]
        to: String,
        /// Schema (ohlcv-1m, ohlcv-1s, trades, mbp-1)
        #[arg(long, default_value = "ohlcv-1m")]
        schema: String,
    },
    /// Show data pipeline status
    Status,
    /// Show data cache inventory
    Cache,
    /// Show live data feed status
    Feeds,
}

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

#[derive(Serialize)]
struct DownloadResult {
    job_id: String,
    status: String,
    estimated_cost_usd: f64,
    message: String,
}

impl HumanReadable for DownloadResult {
    fn print_human(&self) {
        println!("{}", "Download Scheduled".green().bold());
        println!("  Job ID:         {}", self.job_id);
        println!("  Status:         {}", self.status);
        println!(
            "  Est. Cost:      ${:.2}",
            self.estimated_cost_usd
        );
        println!("  Message:        {}", self.message);
    }
}

#[derive(Serialize)]
struct DownloadJobInfo {
    job_id: String,
    status: String,
    dataset: String,
    symbols: Vec<String>,
    start_date: String,
    end_date: String,
    progress_pct: f32,
    bytes_downloaded: u64,
    total_bytes: u64,
    records_count: u64,
    data_quality_score: f64,
    error_message: String,
}

#[derive(Serialize)]
struct StatusResult {
    jobs: Vec<DownloadJobInfo>,
    total_count: u32,
}

impl HumanReadable for StatusResult {
    fn print_human(&self) {
        println!(
            "{} ({} total)",
            "Download Jobs".cyan().bold(),
            self.total_count,
        );
        if self.jobs.is_empty() {
            println!("  No download jobs found.");
            return;
        }
        for job in &self.jobs {
            let status_colored = match job.status.as_str() {
                "COMPLETED" => job.status.green().to_string(),
                "DOWNLOADING" | "VALIDATING" | "UPLOADING" => job.status.yellow().to_string(),
                "FAILED" => job.status.red().to_string(),
                "CANCELLED" => job.status.dimmed().to_string(),
                _ => job.status.clone(),
            };
            println!(
                "  {} [{}] {:.0}%  {}  {} -> {}",
                job.job_id.dimmed(),
                status_colored,
                job.progress_pct,
                job.symbols.join(","),
                job.start_date,
                job.end_date,
            );
            if !job.error_message.is_empty() {
                println!("    Error: {}", job.error_message.red());
            }
        }
    }
}

#[derive(Serialize)]
struct CacheResult {
    message: String,
}

impl HumanReadable for CacheResult {
    fn print_human(&self) {
        println!("{}", "Data Cache".cyan().bold());
        println!("  {}", self.message);
    }
}

#[derive(Serialize)]
struct FeedsResult {
    message: String,
}

impl HumanReadable for FeedsResult {
    fn print_human(&self) {
        println!("{}", "Live Data Feeds".cyan().bold());
        println!("  {}", self.message);
    }
}

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

fn download_status_name(value: i32) -> &'static str {
    use crate::proto::data_acquisition::DownloadStatus;
    match DownloadStatus::try_from(value) {
        Ok(DownloadStatus::Pending) => "PENDING",
        Ok(DownloadStatus::Downloading) => "DOWNLOADING",
        Ok(DownloadStatus::Validating) => "VALIDATING",
        Ok(DownloadStatus::Uploading) => "UPLOADING",
        Ok(DownloadStatus::Completed) => "COMPLETED",
        Ok(DownloadStatus::Failed) => "FAILED",
        Ok(DownloadStatus::Cancelled) => "CANCELLED",
        _ => "UNKNOWN",
    }
}

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

impl DataCommand {
    pub async fn execute(&self, client: &FoxhuntClient, format: OutputFormat) -> Result<()> {
        match &self.action {
            DataAction::Download {
                symbol,
                from,
                to,
                schema,
            } => {
                let response = client
                    .data_acquisition()
                    .schedule_download(crate::proto::data_acquisition::ScheduleDownloadRequest {
                        dataset: "GLBX.MDP3".to_owned(),
                        symbols: vec![symbol.clone()],
                        start_date: from.clone(),
                        end_date: to.clone(),
                        schema: schema.clone(),
                        description: String::new(),
                        tags: Default::default(),
                        priority: 3,
                    })
                    .await?
                    .into_inner();

                let result = DownloadResult {
                    job_id: response.job_id,
                    status: download_status_name(response.status).to_owned(),
                    estimated_cost_usd: response.estimated_cost_usd,
                    message: response.message,
                };
                output::render(&result, format)?;
            }

            DataAction::Status => {
                let response = client
                    .data_acquisition()
                    .list_download_jobs(
                        crate::proto::data_acquisition::ListDownloadJobsRequest {
                            page: 0,
                            page_size: 50,
                            status_filter: 0,
                            start_time: 0,
                            end_time: 0,
                        },
                    )
                    .await?
                    .into_inner();

                let jobs: Vec<DownloadJobInfo> = response
                    .jobs
                    .into_iter()
                    .map(|j| DownloadJobInfo {
                        job_id: j.job_id,
                        status: download_status_name(j.status).to_owned(),
                        dataset: j.dataset,
                        symbols: j.symbols,
                        start_date: j.start_date,
                        end_date: j.end_date,
                        progress_pct: j.progress_percentage,
                        bytes_downloaded: 0,
                        total_bytes: 0,
                        records_count: 0,
                        data_quality_score: 0.0,
                        error_message: String::new(),
                    })
                    .collect();

                let result = StatusResult {
                    total_count: response.total_count,
                    jobs,
                };
                output::render(&result, format)?;
            }

            DataAction::Cache => {
                let result = CacheResult {
                    message:
                        "Data cache managed by MinIO.  Use `fxt data download` to fetch new data."
                            .to_owned(),
                };
                output::render(&result, format)?;
            }

            DataAction::Feeds => {
                let result = FeedsResult {
                    message:
                        "Live feed status requires a running data acquisition service.  Use `fxt service status data-acquisition` for details."
                            .to_owned(),
                };
                output::render(&result, format)?;
            }
        }
        Ok(())
    }
}
