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

use common_contracts::workflow_node::{WorkflowNode, NodeContext, NodeExecutionResult, ArtifactContent, CacheKey};
use common_contracts::data_formatting;
use common_contracts::dtos::{CompanyProfileDto, TimeSeriesFinancialDto};
use crate::state::AppState;
use std::time::Duration;

pub struct MockNode {
    #[allow(dead_code)]
    state: AppState,
}

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

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

    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![
            "mock",
            "company_data",
            symbol,
            "all"
        ];
        
        let cache_key = CacheKey(key_parts.join(":"));
        // Mock data is static, but we can cache it for 1 hour
        let ttl = Duration::from_secs(3600);

        Some((cache_key, ttl))
    }

    async fn execute(&self, _ctx: &NodeContext, config: &Value) -> Result<NodeExecutionResult> {
        let mode = config.get("simulation_mode").and_then(|v| v.as_str()).unwrap_or("normal");

        if mode == "hang" {
            tracing::info!("Simulating Hang (Sleep 600s)...");
            tokio::time::sleep(Duration::from_secs(600)).await;
        }
        
        if mode == "crash" {
             tracing::info!("Simulating Crash (Process Exit)...");
             tokio::time::sleep(Duration::from_secs(1)).await;
             std::process::exit(1); 
        }

        let symbol = config.get("symbol").and_then(|s| s.as_str()).unwrap_or("MOCK").to_string();
        
        // Generate Dummy Data
        let profile = CompanyProfileDto {
            symbol: symbol.clone(),
            name: format!("Mock Company {}", symbol),
            industry: Some("Testing".to_string()),
            list_date: Some(NaiveDate::from_ymd_opt(2000, 1, 1).unwrap()),
            additional_info: Some(json!({
                "description": "This is a mock company for testing purposes.",
                "market_cap": 1000000000.0,
                "currency": "USD",
                "country": "US"
            })),
            updated_at: Some(chrono::Utc::now()),
        };

        let date = NaiveDate::from_ymd_opt(2023, 12, 31).unwrap();
        let financials = vec![
            TimeSeriesFinancialDto {
                symbol: symbol.clone(),
                metric_name: "revenue".to_string(),
                period_date: date,
                value: 1000000.0,
                source: Some("mock".to_string()),
            },
            TimeSeriesFinancialDto {
                symbol: symbol.clone(),
                metric_name: "net_income".to_string(),
                period_date: date,
                value: 500000.0,
                source: Some("mock".to_string()),
            },
            TimeSeriesFinancialDto {
                symbol: symbol.clone(),
                metric_name: "total_assets".to_string(),
                period_date: date,
                value: 2000000.0,
                source: Some("mock".to_string()),
            },
            TimeSeriesFinancialDto {
                symbol: symbol.clone(),
                metric_name: "operating_cash_flow".to_string(),
                period_date: date,
                value: 550000.0,
                source: Some("mock".to_string()),
            },
        ];

        // Prepare 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!("# Mock 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)
    }
}

