// src/corporate/update.rs
use super::{scraper::*, storage::*, helpers::*, types::*, aggregation::*, openfigi::*};
use crate::config::Config;
use crate::scraper::webdriver::ChromeDriverPool;

use chrono::Local;
use std::collections::{HashMap, HashSet};
use std::sync::Arc;
use futures::{stream::{self, StreamExt}};

/// Main function: Full update for all companies (LEI-based) with optimized parallel execution.
///
/// This function coordinates the entire update process:
/// - Loads GLEIF mappings
/// - Builds FIGI-LEI map
/// - Loads existing events
/// - Processes each company: discovers exchanges via FIGI, fetches prices & earnings, aggregates data
/// - Uses the provided shared ChromeDriver pool for efficient parallel scraping
/// - Saves optimized events
///
/// # Arguments
/// * `config` - The application configuration.
/// * `pool` - Shared pool of ChromeDriver instances for scraping.
///
/// # Errors
/// Returns an error if any step in the update process fails.
pub async fn run_full_update(config: &Config, pool: &Arc<ChromeDriverPool>) -> anyhow::Result<()> {
    println!("=== Starting LEI-based corporate full update ===");

    // 1. Load fresh GLEIF ISIN ↔ LEI mapping
    let lei_to_isins: HashMap<String, Vec<String>> = match load_isin_lei_csv().await {
        Ok(map) => map,
        Err(e) => {
            eprintln!("Warning: Could not load GLEIF ISIN↔LEI mapping: {}", e);
            HashMap::new()
        }
    };

    // 2. Load OpenFIGI mapping value lists (cached)
    if let Err(e) = load_figi_type_lists().await {
        eprintln!("Warning: Could not load OpenFIGI type lists: {}", e);
    }

    // 3. Build FIGI → LEI map
    // # Attributes
    // * lei: Structuring the companies by legal dependencies [LEI -> Vec<ISIN>]
    // * figi: metadata with ISIN as key
    let figi_to_lei:HashMap<String, Vec<FigiInfo>> = match build_lei_to_figi_infos(&lei_to_isins).await {
        Ok(map) => map,
        Err(e) => {
            eprintln!("Warning: Could not build FIGI→LEI map: {}", e);
            HashMap::new()
        }
    };

    // 4. Load or build companies
    let mut companies = load_or_build_companies_by_name(&figi_to_lei).await?;
    println!("Processing {} companies", companies.len());

    // 5. Load existing earnings events (for change detection)
    let today = Local::now().format("%Y-%m-%d").to_string();
    let mut existing_events = match load_existing_events().await {
        Ok(events) => events,
        Err(e) => {
            eprintln!("Warning: Could not load existing events: {}", e);
            HashMap::new()
        }
    };

    // 5. Use the provided pool (no need to create a new one)
    let pool_size = pool.get_number_of_instances(); // Use the size from the shared pool

    // Process companies in parallel using the shared pool
    /*let results: Vec<_> = stream::iter(companies.into_iter())
        .map(|company| {
            let pool_clone = pool.clone();
            async move {
                process_company_data(&company, &pool_clone, &mut existing_events).await
            }
        })
        .buffer_unordered(pool_size)
        .collect().await;

    // Handle results (e.g., collect changes)
    let mut all_changes = Vec::new();
    for result in results {
        if let Ok(ProcessResult { changes }) = result {
            all_changes.extend(changes);
        }
    }*/

    save_optimized_events(existing_events).await?;
    //save_changes(&all_changes).await?;

    //println!("Corporate update complete — {} changes detected", all_changes.len());
    Ok(())
}

async fn assign_leis_from_figi(
    companies: &mut [CompanyMetadata],
    lei_to_isins: &HashMap<String, Vec<String>>
) -> anyhow::Result<()> {
    for company in companies {
        let figi_infos = company.figi.as_ref().map_or(&[][..], |v| &v[..]);
        let isins: Vec<String> = figi_infos
            .iter()
            .map(|f| f.isin.clone())
            .collect::<HashSet<_>>()
            .into_iter()
            .collect();

        // Try to find LEI by any known ISIN
        for isin in &isins {
            for (lei, isins) in lei_to_isins.iter() {
                if isins.contains(isin) {
                    company.lei = lei.clone();
                    let name = figi_infos.first().map(|f| f.name.as_str()).unwrap_or("Unknown");
                    println!("Found real LEI {} for {}", lei, name);
                    break;
                }
            }
            if !company.lei.is_empty() { break; }
        }
    }
    Ok(())
}

pub struct ProcessResult {
    pub changes: Vec<CompanyEventChange>,
}

pub fn process_batch(
    new_events: &[CompanyEvent],
    existing: &mut HashMap<String, CompanyEvent>,
    today: &str,
) -> ProcessResult {
    let mut changes = Vec::new();

    for new in new_events {
        let key = event_key(new);

        if let Some(old) = existing.get(&key) {
            changes.extend(detect_changes(old, new, today));
            existing.insert(key, new.clone());
            continue;
        }

        // Check for time change on same date
        let date_key = format!("{}|{}", new.ticker, new.date);
        let mut found_old = None;
        for (k, e) in existing.iter() {
            if format!("{}|{}", e.ticker, e.date) == date_key && k != &key {
                found_old = Some((k.clone(), e.clone()));
                break;
            }
        }

        if let Some((old_key, old_event)) = found_old {
            if new.date.as_str() > today {
                changes.push(CompanyEventChange {
                    ticker: new.ticker.clone(),
                    date: new.date.clone(),
                    field_changed: "time".to_string(),
                    old_value: old_event.time.clone(),
                    new_value: new.time.clone(),
                    detected_at: Local::now().format("%Y-%m-%d %H:%M:%S").to_string(),
                });
            }
            existing.remove(&old_key);
        }

        existing.insert(key, new.clone());
    }

    ProcessResult { changes }
}