//! 数据模型转换模块（AlphaVantage 专用映射）
//!
//! 使用 AlphaVantage HTTP JSON → 我们的 DTO 的强类型转换。
//!

use anyhow::{anyhow, Context};
use chrono::{NaiveDate, Utc};
use common_contracts::dtos::{CompanyProfileDto, RealtimeQuoteDto, TimeSeriesFinancialDto};
use serde_json::Value;

// --- Helpers ---
fn parse_date(s: &str) -> anyhow::Result<NaiveDate> {
    NaiveDate::parse_from_str(s, "%Y-%m-%d").map_err(|e| anyhow!("Invalid date '{}': {}", s, e))
}

fn parse_f64_opt(v: &Value) -> Option<f64> {
    v.as_str().and_then(|s| s.parse::<f64>().ok())
}

fn required_str<'a>(v: &'a Value, key: &str) -> anyhow::Result<&'a str> {
    v.get(key)
        .and_then(|x| x.as_str())
        .ok_or_else(|| anyhow!("Missing required string field '{}'", key))
}

// --- CompanyProfile Mapping ---
pub fn parse_company_profile(v: Value) -> anyhow::Result<CompanyProfileDto> {
    Ok(CompanyProfileDto {
        symbol: required_str(&v, "Symbol")?.to_string(),
        name: required_str(&v, "Name")?.to_string(),
        list_date: v
            .get("IPODate")
            .and_then(|x| x.as_str())
            .map(parse_date)
            .transpose()?,
        industry: v.get("Industry").and_then(|x| x.as_str()).map(|s| s.to_string()),
        additional_info: Some(serde_json::json!({
            "exchange": v.get("Exchange"),
            "currency": v.get("Currency"),
            "country": v.get("Country"),
            "sector": v.get("Sector"),
            "market_capitalization": v.get("MarketCapitalization"),
            "pe_ratio": v.get("PERatio"),
            "beta": v.get("Beta")
        })),
        updated_at: None,
    })
}

// --- Financials Mapping ---
pub struct CombinedFinancials {
    pub income: Value,
    pub balance_sheet: Value,
    pub cash_flow: Value,
}

pub fn parse_financials(cf: CombinedFinancials) -> anyhow::Result<Vec<TimeSeriesFinancialDto>> {
    let symbol = required_str(&cf.income, "symbol")?.to_string();
    let income_reports = cf
        .income
        .get("annualReports")
        .and_then(|x| x.as_array())
        .context("Missing annualReports in income statement")?;

    let balance_reports = cf
        .balance_sheet
        .get("annualReports")
        .and_then(|x| x.as_array())
        .unwrap_or(&vec![])
        .clone();

    let cashflow_reports = cf
        .cash_flow
        .get("annualReports")
        .and_then(|x| x.as_array())
        .unwrap_or(&vec![])
        .clone();

    let mut out: Vec<TimeSeriesFinancialDto> = Vec::new();

    for inc in income_reports {
        let date_str = required_str(inc, "fiscalDateEnding")?;
        let period_date = parse_date(date_str)?;

        // income metrics
        if let Some(v) = parse_f64_opt(&inc["totalRevenue"]) {
            out.push(TimeSeriesFinancialDto {
                symbol: symbol.clone(),
                metric_name: "revenue".to_string(),
                period_date,
                value: v,
                source: Some("alphavantage".to_string()),
            });
        }
        if let Some(v) = parse_f64_opt(&inc["netIncome"]) {
            out.push(TimeSeriesFinancialDto {
                symbol: symbol.clone(),
                metric_name: "net_income".to_string(),
                period_date,
                value: v,
                source: Some("alphavantage".to_string()),
            });
        }

        // match balance and cashflow by fiscalDateEnding
        if let Some(bal) = balance_reports
            .iter()
            .find(|r| r.get("fiscalDateEnding") == Some(&Value::String(date_str.to_string())))
        {
            if let Some(v) = parse_f64_opt(&bal["totalAssets"]) {
                out.push(TimeSeriesFinancialDto {
                    symbol: symbol.clone(),
                    metric_name: "total_assets".to_string(),
                    period_date,
                    value: v,
                    source: Some("alphavantage".to_string()),
                });
            }
            if let Some(v) = parse_f64_opt(&bal["totalLiabilities"]) {
                out.push(TimeSeriesFinancialDto {
                    symbol: symbol.clone(),
                    metric_name: "total_liabilities".to_string(),
                    period_date,
                    value: v,
                    source: Some("alphavantage".to_string()),
                });
            }
        }

        if let Some(cf) = cashflow_reports
            .iter()
            .find(|r| r.get("fiscalDateEnding") == Some(&Value::String(date_str.to_string())))
        {
            if let Some(v) = parse_f64_opt(&cf["operatingCashflow"]) {
                out.push(TimeSeriesFinancialDto {
                    symbol: symbol.clone(),
                    metric_name: "operating_cashflow".to_string(),
                    period_date,
                    value: v,
                    source: Some("alphavantage".to_string()),
                });
            }
        }
    }

    Ok(out)
}

// --- RealtimeQuote Mapping ---
pub fn parse_realtime_quote(v: Value, market: &str) -> anyhow::Result<RealtimeQuoteDto> {
    let q = v
        .get("Global Quote")
        .ok_or_else(|| anyhow!("Missing 'Global Quote' object"))?;

    let symbol = required_str(q, "01. symbol")?.to_string();
    let price = parse_f64_opt(&q["05. price"]).context("Invalid price")?;
    let open_price = parse_f64_opt(&q["02. open"]);
    let high_price = parse_f64_opt(&q["03. high"]);
    let low_price = parse_f64_opt(&q["04. low"]);
    let prev_close = parse_f64_opt(&q["08. previous close"]);
    let change = parse_f64_opt(&q["09. change"]);
    let change_percent = q
        .get("10. change percent")
        .and_then(|x| x.as_str())
        .and_then(|s| s.trim_end_matches('%').parse::<f64>().ok());
    let volume = q
        .get("06. volume")
        .and_then(|x| x.as_str())
        .and_then(|s| s.parse::<i64>().ok());

    Ok(RealtimeQuoteDto {
        symbol,
        market: market.to_string(),
        ts: Utc::now(),
        price,
        open_price,
        high_price,
        low_price,
        prev_close,
        change,
        change_percent,
        volume,
        source: Some("alphavantage".to_string()),
    })
}
