use async_trait::async_trait;
use anyhow::{Result, anyhow, Context};
use serde_json::{json, Value};
use std::collections::HashMap;
use std::time::Duration;
use tokio::time::sleep;

use common_contracts::workflow_node::{WorkflowNode, NodeContext, NodeExecutionResult, ArtifactContent, CacheKey};
use common_contracts::data_formatting;
use crate::state::AppState;
use crate::mapping;

pub struct AlphavantageNode {
    state: AppState,
}

impl AlphavantageNode {
    pub fn new(state: AppState) -> Self {
        Self { state }
    }
}

#[async_trait]
impl WorkflowNode for AlphavantageNode {
    fn node_type(&self) -> &str {
        "alphavantage"
    }

    fn get_cache_config(&self, config: &Value) -> Option<(CacheKey, Duration)> {
        let symbol = config.get("symbol").and_then(|s| s.as_str())?;
        
        let key_parts = vec![
            "alphavantage",
            "company_data",
            symbol,
            "all"
        ];
        
        let cache_key = CacheKey(key_parts.join(":"));
        let ttl = Duration::from_secs(86400); // 24h

        Some((cache_key, ttl))
    }

    async fn execute(&self, _ctx: &NodeContext, config: &Value) -> Result<NodeExecutionResult> {
        let symbol = config.get("symbol").and_then(|s| s.as_str()).unwrap_or("").to_string();
        
        if symbol.is_empty() {
            return Err(anyhow!("Missing symbol in config"));
        }

        // 1. Get Provider (MCP Client)
        let provider = self.state.get_provider().await
            .ok_or_else(|| anyhow!("Alphavantage Provider not initialized"))?;

        // 2. Fetch Data via MCP (Sequential with Rate Limit Protection)
        
        // COMPANY_OVERVIEW
        let overview_json = provider.query("COMPANY_OVERVIEW", &[("symbol", &symbol)]).await
            .context("Failed to fetch COMPANY_OVERVIEW")?;
        check_av_response(&overview_json)?;
        sleep(Duration::from_secs(2)).await;

        // GLOBAL_QUOTE
        let _quote_json = provider.query("GLOBAL_QUOTE", &[("symbol", &symbol), ("datatype", "json")]).await
            .context("Failed to fetch GLOBAL_QUOTE")?;
        // check_av_response(&quote_json)?; // Quote not strictly required for Profile/Financials report
        sleep(Duration::from_secs(2)).await;

        // INCOME_STATEMENT
        let income_json = provider.query("INCOME_STATEMENT", &[("symbol", &symbol)]).await
            .context("Failed to fetch INCOME_STATEMENT")?;
        check_av_response(&income_json)?;
        sleep(Duration::from_secs(2)).await;

        // BALANCE_SHEET
        let balance_json = provider.query("BALANCE_SHEET", &[("symbol", &symbol)]).await
            .context("Failed to fetch BALANCE_SHEET")?;
        check_av_response(&balance_json)?;
        sleep(Duration::from_secs(2)).await;

        // CASH_FLOW
        let cashflow_json = provider.query("CASH_FLOW", &[("symbol", &symbol)]).await
            .context("Failed to fetch CASH_FLOW")?;
        check_av_response(&cashflow_json)?;

        // 3. Parse & Combine
        let profile = mapping::parse_company_profile(overview_json)?;
        
        let combined = mapping::CombinedFinancials {
            income: income_json,
            balance_sheet: balance_json,
            cash_flow: cashflow_json,
        };
        let financials = mapping::parse_financials(combined)?;

        // 4. Artifacts
        let mut artifacts = HashMap::new();
        artifacts.insert("profile.json".to_string(), json!(profile).into());
        artifacts.insert("financials.json".to_string(), json!(financials).into());

        Ok(NodeExecutionResult {
            artifacts,
            meta_summary: Some(json!({
                "symbol": symbol,
                "records": financials.len()
            })),
        })
    }

    fn render_report(&self, result: &NodeExecutionResult) -> Result<String> {
        let profile_json = match result.artifacts.get("profile.json") {
             Some(ArtifactContent::Json(v)) => v,
             _ => return Err(anyhow!("Missing profile.json")),
        };
        let financials_json = match result.artifacts.get("financials.json") {
             Some(ArtifactContent::Json(v)) => v,
             _ => return Err(anyhow!("Missing financials.json")),
        };

        let symbol = profile_json["symbol"].as_str().unwrap_or("Unknown");

        let mut report_md = String::new();
        report_md.push_str(&format!("# Alphavantage Data Report: {}\n\n", symbol));
        
        report_md.push_str("## Company Profile\n\n");
        report_md.push_str(&data_formatting::format_data(profile_json));
        report_md.push_str("\n\n");

        report_md.push_str("## Financial Statements\n\n");
        report_md.push_str(&data_formatting::format_data(financials_json));

        Ok(report_md)
    }
}

fn check_av_response(v: &Value) -> Result<()> {
    if let Some(note) = v.get("Note").and_then(|s| s.as_str()) {
        return Err(anyhow!("AlphaVantage Rate Limit: {}", note));
    }
    if let Some(info) = v.get("Information").and_then(|s| s.as_str()) {
        return Err(anyhow!("AlphaVantage Information: {}", info));
    }
    Ok(())
}
