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

use common_contracts::workflow_node::{WorkflowNode, NodeContext, NodeExecutionResult, ArtifactContent, CacheKey};
use common_contracts::persistence_client::PersistenceClient;
use common_contracts::workflow_harness::TaskState;
use crate::state::AppState;
use crate::formatter::{TushareFormatter, TushareMetric};
use std::time::Duration;

pub struct TushareNode {
    state: AppState,
}

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

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

    fn get_cache_config(&self, config: &Value) -> Option<(CacheKey, Duration)> {
        let symbol = config.get("symbol").and_then(|s| s.as_str())?;
        
        // Construct Tuple Key: provider:interface:arg1
        let key_parts = vec![
            "tushare",
            "company_data", // Conceptual interface name
            symbol,
            "all"           // Scope
        ];
        
        let cache_key = CacheKey(key_parts.join(":"));
        // Tushare data is financial reports, valid for at least 7*24 hours
        let ttl = Duration::from_secs(7 * 24 * 60 * 60);

        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();
        let _market = config.get("market").and_then(|s| s.as_str()).unwrap_or("CN").to_string();

        if symbol.is_empty() {
            return Err(anyhow!("Missing symbol in config"));
        }

        // 1. Fetch Data
        let provider = self.state.get_provider().await
            .ok_or_else(|| anyhow!("Tushare Provider not initialized"))?;

        let (profile, financials) = provider.fetch_all_data(&symbol).await
            .context("Failed to fetch data from Tushare")?;

        // 2. DB Cache (Side effect)
        let persistence_url = self.state.get_persistence_url();
        let p_client = PersistenceClient::new(persistence_url);
        if let Err(e) = p_client.upsert_company_profile(profile.clone()).await {
             tracing::warn!("Failed to cache company profile: {}", e);
        }

        // 3. Artifacts
        let mut artifacts = HashMap::new();
        artifacts.insert("profile.json".to_string(), json!(profile).into());
        
        // Format Report directly to markdown
        let metrics: Vec<TushareMetric> = financials.iter().map(|d| TushareMetric {
            metric_name: d.metric_name.clone(),
            period_date: d.period_date.to_string(),
            value: Some(d.value),
        }).collect();

        let formatter = TushareFormatter::new();
        let report_md = formatter.format_to_markdown(&symbol, metrics.clone())
            .context("Failed to format markdown report")?;
            
        artifacts.insert("financials.md".to_string(), ArtifactContent::Text(report_md));
        
        // 4. Dump Metrics for Robustness (Recover from missing financials.md)
        artifacts.insert("_metrics_dump.json".to_string(), json!(metrics).into());

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

    fn render_report(&self, result: &NodeExecutionResult) -> Result<String> {
        match result.artifacts.get("financials.md") {
             Some(ArtifactContent::Text(s)) => Ok(s.clone()),
             _ => {
                // Robustness: Try to regenerate if financials.md is missing (e.g. cache hit but old version or partial cache)
                if let Some(ArtifactContent::Json(json_val)) = result.artifacts.get("_metrics_dump.json") {
                     // Clone value to deserialize
                     if let Ok(metrics) = serde_json::from_value::<Vec<TushareMetric>>(json_val.clone()) {
                          let formatter = TushareFormatter::new();
                          let symbol = result.meta_summary.as_ref()
                                .and_then(|v| v.get("symbol"))
                                .and_then(|v| v.as_str())
                                .unwrap_or("Unknown");
                          
                          tracing::info!("Regenerating financials.md from cached metrics dump for {}", symbol);
                          return formatter.format_to_markdown(symbol, metrics)
                                .context("Failed to regenerate markdown report from metrics");
                     }
                }
                Err(anyhow!("Missing financials.md"))
             }
        }
    }
}

