use anyhow::{Result, anyhow, Context};
use tracing::{info, error};
use common_contracts::workflow_types::{WorkflowTaskCommand, WorkflowTaskEvent, TaskStatus, TaskResult};
use common_contracts::subjects::{NatsSubject, SubjectMessage};
use common_contracts::dtos::{CompanyProfileDto, TimeSeriesFinancialDto};
use workflow_context::WorkerContext;
use crate::state::AppState;
use serde_json::json;
use futures_util::StreamExt;
use chrono::NaiveDate;

pub async fn run_consumer(state: AppState) -> Result<()> {
    info!("Starting Mock Provider message consumer...");
    let client = async_nats::connect(&state.config.nats_addr).await?;
    
    let routing_key = "provider.mock".to_string();
    let subject = NatsSubject::WorkflowCommand(routing_key).to_string();
    let mut subscriber = client.subscribe(subject.clone()).await?;

    info!("Workflow Consumer started on '{}'", subject);

    while let Some(message) = subscriber.next().await {
        info!("Received Workflow NATS message.");
        let state_clone = state.clone();
        let client_clone = client.clone();

        tokio::spawn(async move {
            match serde_json::from_slice::<WorkflowTaskCommand>(&message.payload) {
                Ok(cmd) => {
                    if let Err(e) = handle_workflow_command(state_clone, client_clone, cmd).await {
                        error!("Error handling workflow command: {:?}", e);
                    }
                }
                Err(e) => {
                    error!("Failed to deserialize workflow message: {}", e);
                }
            }
        });
    }
    Ok(())
}

async fn handle_workflow_command(_state: AppState, nats: async_nats::Client, cmd: WorkflowTaskCommand) -> Result<()> {
    info!("Processing mock workflow command: task_id={}", cmd.task_id);

    let symbol_code = cmd.config.get("symbol").and_then(|s| s.as_str()).unwrap_or("MOCK").to_string();
    
    // Generate Dummy Data
    let profile = CompanyProfileDto {
        symbol: symbol_code.clone(),
        name: format!("Mock Company {}", symbol_code),
        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()),
    };

    // Create Normalized Financials
    let date = NaiveDate::from_ymd_opt(2023, 12, 31).unwrap();
    let financials = vec![
        TimeSeriesFinancialDto {
            symbol: symbol_code.clone(),
            metric_name: "revenue".to_string(),
            period_date: date,
            value: 1000000.0,
            source: Some("mock".to_string()),
        },
        TimeSeriesFinancialDto {
            symbol: symbol_code.clone(),
            metric_name: "net_income".to_string(),
            period_date: date,
            value: 500000.0,
            source: Some("mock".to_string()),
        },
        TimeSeriesFinancialDto {
            symbol: symbol_code.clone(),
            metric_name: "gross_profit".to_string(),
            period_date: date,
            value: 800000.0,
            source: Some("mock".to_string()),
        },
        TimeSeriesFinancialDto {
            symbol: symbol_code.clone(),
            metric_name: "operating_income".to_string(),
            period_date: date,
            value: 600000.0,
            source: Some("mock".to_string()),
        },
        TimeSeriesFinancialDto {
            symbol: symbol_code.clone(),
            metric_name: "eps".to_string(),
            period_date: date,
            value: 5.0,
            source: Some("mock".to_string()),
        },
        TimeSeriesFinancialDto {
            symbol: symbol_code.clone(),
            metric_name: "total_assets".to_string(),
            period_date: date,
            value: 2000000.0,
            source: Some("mock".to_string()),
        },
        TimeSeriesFinancialDto {
            symbol: symbol_code.clone(),
            metric_name: "total_liabilities".to_string(),
            period_date: date,
            value: 1000000.0,
            source: Some("mock".to_string()),
        },
        TimeSeriesFinancialDto {
            symbol: symbol_code.clone(),
            metric_name: "total_equity".to_string(),
            period_date: date,
            value: 1000000.0,
            source: Some("mock".to_string()),
        },
        TimeSeriesFinancialDto {
            symbol: symbol_code.clone(),
            metric_name: "operating_cash_flow".to_string(),
            period_date: date,
            value: 550000.0,
            source: Some("mock".to_string()),
        },
        TimeSeriesFinancialDto {
            symbol: symbol_code.clone(),
            metric_name: "free_cash_flow".to_string(),
            period_date: date,
            value: 450000.0,
            source: Some("mock".to_string()),
        }
    ];

    // Write to VGCS
    let root_path = cmd.storage.root_path.clone();
    let req_id = cmd.request_id.to_string();
    let base_commit = cmd.context.base_commit.clone().unwrap_or_default();
    let symbol_code_clone = symbol_code.clone();
    
    // Clone data for the blocking task
    let profile_clone = profile.clone();
    let financials_clone = financials.clone();

    let commit_result = tokio::task::spawn_blocking(move || -> Result<String> {
        let mut ctx = WorkerContext::new(&root_path, &req_id, &base_commit);

        let base_dir = format!("raw/mock/{}", symbol_code_clone);
        
        let profile_json = serde_json::to_string_pretty(&profile_clone)
            .context("Failed to serialize profile")?;
        ctx.write_file(&format!("{}/profile.json", base_dir), &profile_json)?;
        
        let financials_json = serde_json::to_string_pretty(&financials_clone)
            .context("Failed to serialize financials")?;
        ctx.write_file(&format!("{}/financials.json", base_dir), &financials_json)?;

        ctx.commit(&format!("Fetched Mock data for {}", symbol_code_clone))
    }).await;

    let new_commit = match commit_result {
        Ok(res) => match res {
            Ok(c) => c,
            Err(e) => return send_failure(&nats, &cmd, &format!("VGCS failed: {}", e)).await,
        },
        Err(e) => return send_failure(&nats, &cmd, &format!("Task join error: {}", e)).await,
    };
    
    info!("Task {} completed. New commit: {}", cmd.task_id, new_commit);

    // Send Success Event
    let event = WorkflowTaskEvent {
        request_id: cmd.request_id,
        task_id: cmd.task_id,
        status: TaskStatus::Completed,
        result: Some(TaskResult {
            new_commit: Some(new_commit),
            error: None,
            summary: Some(json!({
                "symbol": symbol_code,
                "records": financials.len()
            })),
        }),
    };
    
    publish_event(&nats, event).await
}

async fn send_failure(nats: &async_nats::Client, cmd: &WorkflowTaskCommand, error_msg: &str) -> Result<()> {
    error!("Task {} failed: {}", cmd.task_id, error_msg);
    let event = WorkflowTaskEvent {
        request_id: cmd.request_id,
        task_id: cmd.task_id.clone(),
        status: TaskStatus::Failed,
        result: Some(TaskResult {
            new_commit: None,
            error: Some(error_msg.to_string()),
            summary: None,
        }),
    };
    publish_event(nats, event).await
}

async fn publish_event(nats: &async_nats::Client, event: WorkflowTaskEvent) -> Result<()> {
    let subject = event.subject().to_string();
    let payload = serde_json::to_vec(&event)?;
    nats.publish(subject, payload.into()).await?;
    Ok(())
}
