use std::sync::Arc;

use common_contracts::{
    dtos::{CompanyProfileDto, TimeSeriesFinancialDto},
    messages::FetchCompanyDataCommand,
    workflow_harness::StandardFetchWorkflow,
    abstraction::DataProviderLogic,
    persistence_client::PersistenceClient,
};
use crate::error::{Result, AppError};
use crate::state::AppState;

pub struct YFinanceFetcher {
    state: Arc<AppState>,
}

impl YFinanceFetcher {
    pub fn new(state: Arc<AppState>) -> Self {
        Self { state }
    }
}

#[async_trait::async_trait]
impl DataProviderLogic for YFinanceFetcher {
    fn provider_id(&self) -> &str {
        "yfinance"
    }

    async fn fetch_data(&self, symbol: &str) -> anyhow::Result<(CompanyProfileDto, Vec<TimeSeriesFinancialDto>)> {
        // Check dynamic config (Single Source of Truth)
        let client = PersistenceClient::new(self.state.config.data_persistence_service_url.clone());
        let config = client.get_data_sources_config().await?;
        let is_enabled = config.get("yfinance")
            .map(|c| c.enabled)
            .unwrap_or(false);

        if !is_enabled {
             return Err(anyhow::anyhow!("YFinance provider is disabled in dynamic config"));
        }

        // FIX: Use fully qualified call to avoid lifetime inference issue
        let (profile, financials) = self.state.yfinance_provider.fetch_all_data(symbol).await
            .map_err(|e| anyhow::anyhow!("YFinance API error: {}", e))?;
            
        Ok((profile, financials))
    }
}

pub async fn handle_fetch_command(
    state: AppState,
    command: FetchCompanyDataCommand,
    _publisher: async_nats::Client, // Deprecated: harness creates its own scoped connection
) -> Result<()> {
    let state_arc = Arc::new(state);
    let fetcher = Arc::new(YFinanceFetcher::new(state_arc.clone()));

    // Use standard workflow
    // Note: YFinance worker signature was slightly different (no completion tx), 
    // but StandardFetchWorkflow handles Option<Sender>.
    StandardFetchWorkflow::run(
        state_arc,
        fetcher,
        command,
        None 
    ).await.map_err(|e| AppError::Internal(e.to_string()))?;

    Ok(())
}

#[cfg(test)]
mod integration_tests {
    use super::*;
    use crate::config::AppConfig;
    use crate::state::AppState;
    use common_contracts::symbol_utils::{CanonicalSymbol, Market};
    use common_contracts::config_models::{DataSourceConfig, DataSourceProvider};
    use common_contracts::observability::{TaskProgress, ObservabilityTaskStatus};
    use uuid::Uuid;
    use chrono::Utc;

    #[tokio::test]
    async fn test_yfinance_fetch_flow() {
        if std::env::var("NATS_ADDR").is_err() {
             println!("Skipping integration test (no environment)");
             return;
        }

        let config = AppConfig::load().expect("Failed to load config");
        let mut config_enabled = config.clone();
        config_enabled.yfinance_enabled = true;
        let state = AppState::new(config_enabled);

        // 1. Enable YFinance in Persistence Service
        let persistence_client = PersistenceClient::new(config.data_persistence_service_url.clone());
        let mut current_config = persistence_client.get_data_sources_config().await.unwrap_or_default();
        current_config.insert("yfinance".to_string(), DataSourceConfig {
            provider: DataSourceProvider::Yfinance,
            enabled: true,
            api_key: None,
            api_url: None,
        });
        persistence_client.update_data_sources_config(&current_config).await
            .expect("Failed to enable YFinance in persistence");

        // 2. Construct Command
        let request_id = Uuid::new_v4();
        let cmd = FetchCompanyDataCommand {
            request_id,
            symbol: CanonicalSymbol::new("MSFT", &Market::US),
            market: "US".to_string(),
            template_id: Some("default".to_string()),
            output_path: None,
        };
        
        // Init task
        state.tasks.insert(request_id, TaskProgress {
             request_id,
             task_name: "yfinance:MSFT".to_string(),
             status: ObservabilityTaskStatus::Queued,
             progress_percent: 0,
             details: "Init".to_string(),
             started_at: Utc::now()
        });
        
        // 3. NATS (Dummy client for signature compatibility, though harness uses its own)
        let nats_client = async_nats::connect(&config.nats_addr).await
            .expect("Failed to connect to NATS");

        // 4. Run
        let result = handle_fetch_command(state.clone(), cmd, nats_client).await;

        // 5. Assert
        assert!(result.is_ok(), "Worker execution failed: {:?}", result.err());
        
        let task = state.tasks.get(&request_id).expect("Task should exist");
        assert_eq!(task.status, ObservabilityTaskStatus::Completed);
    }
}
