use anyhow::{Result, anyhow, Context};
use tracing::{info, error, warn};
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, OutputFormat};
use crate::state::AppState;
use crate::tushare::TushareDataProvider;
use serde_json::json;
use std::sync::Arc;

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

    // 1. Parse Config
    let symbol_code = cmd.config.get("symbol").and_then(|s| s.as_str()).unwrap_or("").to_string();
    let market = cmd.config.get("market").and_then(|s| s.as_str()).unwrap_or("CN").to_string();
    
    if symbol_code.is_empty() {
        return send_failure(&nats, &cmd, "Missing symbol in config").await;
    }

    // 2. Initialize Worker Context
    // Note: We use the provided base_commit. If it's empty, it means start from scratch (or empty repo).
    // We need to mount the volume.
    let root_path = cmd.storage.root_path.clone();
    
    let mut ctx = match WorkerContext::init(&cmd.request_id.to_string(), &root_path, cmd.context.base_commit.as_deref()) {
        Ok(c) => c,
        Err(e) => return send_failure(&nats, &cmd, &format!("Failed to init context: {}", e)).await,
    };

    // 3. Fetch Data (with Cache)
    let fetch_result = fetch_and_cache(&state, &symbol_code, &market).await;
    
    let (profile, financials) = match fetch_result {
        Ok(data) => data,
        Err(e) => return send_failure(&nats, &cmd, &format!("Fetch failed: {}", e)).await,
    };

    // 4. Write to VGCS
    // Organize data in a structured way
    let base_dir = format!("raw/tushare/{}", symbol_code);
    
    if let Err(e) = ctx.write_file(&format!("{}/profile.json", base_dir), &profile, OutputFormat::Json) {
        return send_failure(&nats, &cmd, &format!("Failed to write profile: {}", e)).await;
    }
    
    if let Err(e) = ctx.write_file(&format!("{}/financials.json", base_dir), &financials, OutputFormat::Json) {
        return send_failure(&nats, &cmd, &format!("Failed to write financials: {}", e)).await;
    }

    // 5. Commit
    let new_commit = match ctx.commit(&format!("Fetched Tushare data for {}", symbol_code)) {
        Ok(c) => c,
        Err(e) => return send_failure(&nats, &cmd, &format!("Commit failed: {}", e)).await,
    };
    
    info!("Task {} completed. New commit: {}", cmd.task_id, new_commit);

    // 6. 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 fetch_and_cache(state: &AppState, symbol: &str, _market: &str) -> Result<(CompanyProfileDto, Vec<TimeSeriesFinancialDto>)> {
    // 1. Get Provider (which holds the API token)
    let provider = state.get_provider().await
        .ok_or_else(|| anyhow!("Tushare Provider not initialized (missing API Token?)"))?;

    // 2. Call fetch
    let (profile, financials) = provider.fetch_all_data(symbol).await
        .context("Failed to fetch data from Tushare")?;
        
    // 3. Write to DB Cache
    // Note: PersistenceClient is not directly in AppState struct definition in `state.rs` I read.
    // Let's check `state.rs` again. It implements TaskState which has `get_persistence_url`.
    // We should instantiate PersistenceClient on the fly or add it to AppState.
    
    // For now, let's create a client on the fly to avoid changing AppState struct everywhere.
    use common_contracts::persistence_client::PersistenceClient;
    use common_contracts::workflow_harness::TaskState; // For get_persistence_url
    
    let persistence_url = state.get_persistence_url();
    let p_client = PersistenceClient::new(persistence_url);
    
    if let Err(e) = p_client.save_company_profile(&profile).await {
        warn!("Failed to cache company profile: {}", e);
    }
    
    // Batch save financials logic is missing in PersistenceClient (based on context).
    // If it existed, we would call it here.
    
    Ok((profile, financials))
}

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(())
}

