use crate::error::{Result, AppError};
use crate::mapping::{CombinedFinancials, parse_company_profile, parse_financials, parse_realtime_quote};
use crate::persistence::PersistenceClient;
use crate::state::{AppState, TaskStore};
use anyhow::Context;
use chrono::{Utc, Datelike};
use common_contracts::messages::{FetchCompanyDataCommand, FinancialsPersistedEvent};
use common_contracts::observability::TaskProgress;
use tracing::{error, info, instrument, warn};
use uuid::Uuid;
use serde_json::Value;

#[instrument(skip(state, command, publisher), fields(request_id = %command.request_id, symbol = %command.symbol))]
pub async fn handle_fetch_command(
    state: AppState,
    command: FetchCompanyDataCommand,
    publisher: async_nats::Client,
) -> Result<()> {
    info!("Handling fetch data command.");

    let task = TaskProgress {
        request_id: command.request_id,
        task_name: format!("fetch_data_for_{}", command.symbol),
        status: "in_progress".to_string(),
        progress_percent: 0,
        details: "Initializing...".to_string(),
        started_at: Utc::now(),
    };
    state.tasks.insert(command.request_id, task);

    let client = match state.get_provider().await {
        Some(p) => p,
        None => {
            let reason = "Execution failed: Alphavantage provider is not available (misconfigured).".to_string();
            error!("{}", reason);
            update_task_progress(
                &state.tasks,
                command.request_id,
                100,
                &reason,
            ).await;
            return Err(AppError::ProviderNotAvailable(reason));
        }
    };

    let persistence_client =
        PersistenceClient::new(state.config.data_persistence_service_url.clone());
    let symbol = command.symbol.clone();

    // Symbol conversion for Chinese stocks
    let av_symbol = if symbol.ends_with(".SH") {
        symbol.replace(".SH", ".SS")
    } else {
        symbol.clone()
    };
    info!("Using symbol for AlphaVantage: {}", av_symbol);

    update_task_progress(
        &state.tasks,
        command.request_id,
        10,
        "Fetching from AlphaVantage...",
    )
    .await;

    // --- 1. Fetch all data in parallel ---
    let (overview_json, income_json, balance_json, cashflow_json, quote_json) = {
        let params_overview = vec![("symbol", av_symbol.as_str())];
        let params_income = vec![("symbol", av_symbol.as_str())];
        let params_balance = vec![("symbol", av_symbol.as_str())];
        let params_cashflow = vec![("symbol", av_symbol.as_str())];
        // Add datatype=json to force JSON response if supported (or at least Python-dict like)
        let params_quote = vec![("symbol", av_symbol.as_str()), ("datatype", "json")];
        
        let overview_task = client.query("COMPANY_OVERVIEW", &params_overview);
        let income_task = client.query("INCOME_STATEMENT", &params_income);
        let balance_task = client.query("BALANCE_SHEET", &params_balance);
        let cashflow_task = client.query("CASH_FLOW", &params_cashflow);
        let quote_task = client.query("GLOBAL_QUOTE", &params_quote);

        match tokio::try_join!(
            overview_task,
            income_task,
            balance_task,
            cashflow_task,
            quote_task
        ) {
            Ok(data) => data,
            Err(e) => {
                let error_msg = format!("Failed to fetch data from AlphaVantage: {}", e);
                error!(error_msg);
                update_task_progress(&state.tasks, command.request_id, 100, &error_msg).await;
                return Err(e);
            }
        }
    };

    update_task_progress(
        &state.tasks,
        command.request_id,
        50,
        "Data fetched, transforming and persisting...",
    )
    .await;

    // --- 2. Transform and persist data ---
    // Profile
    // Check if overview_json is empty (Symbol field check)
    if let Some(_symbol_val) = overview_json.get("Symbol") {
        match parse_company_profile(overview_json) {
            Ok(profile_to_persist) => {
                persistence_client
                    .upsert_company_profile(profile_to_persist)
                    .await?;
            },
            Err(e) => {
                 warn!("Failed to parse CompanyProfile: {}", e);
            }
        }
    } else {
        warn!("CompanyProfile data is empty or missing 'Symbol' for {}, skipping persistence.", av_symbol);
    }

    // Financials
    let mut years_updated: Vec<u16> = Vec::new();
    // Only attempt to parse financials if we have data (simple check if income statement has annualReports)
    if income_json.get("annualReports").is_some() {
        let combined_financials = CombinedFinancials {
            income: income_json,
            balance_sheet: balance_json,
            cash_flow: cashflow_json,
        };
        match parse_financials(combined_financials) {
            Ok(financials_to_persist) => {
                 if !financials_to_persist.is_empty() {
                    years_updated = financials_to_persist
                        .iter()
                        .map(|f| f.period_date.year() as u16)
                        .collect();
                    persistence_client
                        .batch_insert_financials(financials_to_persist)
                        .await?;
                 }
            },
             Err(e) => {
                 warn!("Failed to parse Financials: {}", e);
            }
        }
    } else {
         warn!("Financial data missing for {}, skipping.", av_symbol);
    }

    // Quote
    // Fix Python-dict string if necessary
    let fixed_quote_json = if let Some(s) = quote_json.as_str() {
        if s.trim().starts_with("{'Global Quote'") {
             // Attempt to replace single quotes with double quotes
             // Note: This is a naive fix but works for the expected format
             let fixed = s.replace("'", "\"");
             match serde_json::from_str::<Value>(&fixed) {
                 Ok(v) => v,
                 Err(e) => {
                     warn!("Failed to fix/parse quoted JSON string: {}. Error: {}", s, e);
                     quote_json // fallback to original
                 }
             }
        } else {
            quote_json
        }
    } else {
        quote_json
    };

    match parse_realtime_quote(fixed_quote_json, &command.market) {
        Ok(mut quote_to_persist) => {
            // Restore original symbol if we converted it
            quote_to_persist.symbol = command.symbol.clone();
            persistence_client
                .upsert_realtime_quote(quote_to_persist)
                .await?;
        },
        Err(e) => {
             warn!("Failed to parse RealtimeQuote: {}", e);
        }
    }

    update_task_progress(
        &state.tasks,
        command.request_id,
        90,
        "Data persisted, publishing events...",
    )
    .await;

    // --- 3. Publish events ---
    // Only publish if we actually updated something
    // Actually, we should publish event even if partial, to signal completion?
    // The command is "FetchCompanyData", implies success if we fetched *available* data.
    
    let event = FinancialsPersistedEvent {
        request_id: command.request_id,
        symbol: command.symbol,
        years_updated,
    };
    let subject = "financials.persisted".to_string(); // NATS subject
    publisher
        .publish(subject, serde_json::to_vec(&event).unwrap().into())
        .await?;

    state.tasks.remove(&command.request_id);
    info!("Task completed successfully (Partial data may be missing if provider lacks coverage).");

    Ok(())
}

async fn update_task_progress(tasks: &TaskStore, request_id: Uuid, percent: u8, details: &str) {
    if let Some(mut task) = tasks.get_mut(&request_id) {
        task.progress_percent = percent;
        task.details = details.to_string();
        info!("Task update: {}% - {}", percent, details);
    }
}
