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

pub fn map_profile(summary_json: &Value, symbol: &str) -> Result<CompanyProfileDto, crate::error::AppError> {
    let result = summary_json
        .get("quoteSummary")
        .and_then(|v| v.get("result"))
        .and_then(|v| v.get(0))
        .ok_or_else(|| crate::error::AppError::DataParsing(anyhow!("quoteSummary.result[0] missing")))?;

    let asset_profile = result.get("assetProfile").and_then(|v| v.as_object());
    let quote_type = result.get("quoteType").and_then(|v| v.as_object());

    let name = quote_type
        .and_then(|qt| qt.get("longName"))
        .and_then(|v| v.as_str())
        .unwrap_or(symbol)
        .to_string();
    let industry = asset_profile
        .and_then(|ap| ap.get("industry"))
        .and_then(|v| v.as_str())
        .map(|s| s.to_string());

    // Yahoo 没有可靠的上市日期字段，这里保持 None，附加信息中保留更多细节
    let mut additional = serde_json::Map::new();
    if let Some(ex) = quote_type.and_then(|qt| qt.get("exchange")).and_then(|v| v.as_str()) {
        additional.insert("exchange".to_string(), Value::String(ex.to_string()));
    }
    if let Some(website) = asset_profile.and_then(|ap| ap.get("website")).and_then(|v| v.as_str()) {
        additional.insert("website".to_string(), Value::String(website.to_string()));
    }
    if let Some(desc) = asset_profile.and_then(|ap| ap.get("longBusinessSummary")).and_then(|v| v.as_str()) {
        additional.insert("description".to_string(), Value::String(desc.to_string()));
    }

    Ok(CompanyProfileDto {
        symbol: symbol.to_string(),
        name,
        industry,
        list_date: None,
        additional_info: if additional.is_empty() { None } else { Some(Value::Object(additional)) },
        updated_at: None,
    })
}

pub fn map_financial_statements(financials_json: &Value, symbol: &str) -> Result<Vec<TimeSeriesFinancialDto>, crate::error::AppError> {
    let result = financials_json
        .get("quoteSummary")
        .and_then(|v| v.get("result"))
        .and_then(|v| v.get(0))
        .ok_or_else(|| crate::error::AppError::DataParsing(anyhow!("quoteSummary.result[0] missing")))?;

    fn to_vec<'a>(obj: Option<&'a Value>, outer_key: &str, inner_key: &str) -> Vec<&'a serde_json::Map<String, Value>> {
        obj.and_then(|o| o.get(outer_key))
            .and_then(|v| v.get(inner_key))
            .and_then(|v| v.as_array())
            .map(|arr| arr.iter().filter_map(|item| item.as_object()).collect::<Vec<_>>())
            .unwrap_or_default()
    }

    let income_arr = result.get("incomeStatementHistory");
    let balance_arr = result.get("balanceSheetHistory");
    let cashflow_arr = result.get("cashflowStatementHistory");

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

    // helper to parse endDate
    let parse_period = |obj: &serde_json::Map<String, Value>| -> Option<NaiveDate> {
        if let Some(fmt) = obj.get("endDate").and_then(|v| v.get("fmt")).and_then(|v| v.as_str()) {
            NaiveDate::parse_from_str(fmt, "%Y-%m-%d").ok()
        } else {
            None
        }
    };

    // helper to push
    let mut push_metric = |metric: &str, period: NaiveDate, value: f64| {
        out.push(TimeSeriesFinancialDto {
            symbol: symbol.to_string(),
            metric_name: metric.to_string(),
            period_date: period,
            value,
            source: Some("yahoo".to_string()),
        });
    };

    // Income
    for stmt in to_vec(income_arr, "incomeStatementHistory", "incomeStatementHistory") {
        if let Some(period) = parse_period(stmt) {
            if let Some(v) = stmt.get("totalRevenue").and_then(|v| v.get("raw")).and_then(|v| v.as_f64()) {
                push_metric("revenue", period, v);
            }
            if let Some(v) = stmt.get("netIncome").and_then(|v| v.get("raw")).and_then(|v| v.as_f64()) {
                push_metric("net_income", period, v);
            }
        }
    }

    // Balance
    for stmt in to_vec(balance_arr, "balanceSheetHistory", "balanceSheetStatements") {
        if let Some(period) = parse_period(stmt) {
            if let Some(v) = stmt.get("totalAssets").and_then(|v| v.get("raw")).and_then(|v| v.as_f64()) {
                push_metric("total_assets", period, v);
            }
            if let Some(v) = stmt.get("totalLiab").and_then(|v| v.get("raw")).and_then(|v| v.as_f64()) {
                push_metric("total_liab", period, v);
            }
        }
    }

    // Cashflow
    for stmt in to_vec(cashflow_arr, "cashflowStatementHistory", "cashflowStatements") {
        if let Some(period) = parse_period(stmt) {
            if let Some(v) = stmt.get("totalCashFromOperatingActivities").and_then(|v| v.get("raw")).and_then(|v| v.as_f64()) {
                push_metric("operating_cash_flow", period, v);
            }
            if let Some(v) = stmt.get("capitalExpenditures").and_then(|v| v.get("raw")).and_then(|v| v.as_f64()) {
                // 通常为负值，保持原值以保留确定性
                push_metric("capital_expenditure", period, v);
            }
            // Derived: FCF = OCF - CAPEX
            if let (Some(ocf), Some(capex)) = (
                stmt.get("totalCashFromOperatingActivities").and_then(|v| v.get("raw")).and_then(|v| v.as_f64()),
                stmt.get("capitalExpenditures").and_then(|v| v.get("raw")).and_then(|v| v.as_f64()),
            ) {
                push_metric("__free_cash_flow", period, ocf - capex);
            }
        }
    }

    Ok(out)
}
