// src/corporate/helpers.rs
use super::types::*;
use chrono::{Local, NaiveDate};
use rand::rngs::StdRng;
use rand::prelude::{Rng, SeedableRng, IndexedRandom};

pub fn event_key(e: &CompanyEvent) -> String {
    format!("{}|{}|{}", e.ticker, e.date, e.time)
}

pub fn detect_changes(old: &CompanyEvent, new: &CompanyEvent, today: &str) -> Vec<CompanyEventChange> {
    let mut changes = Vec::new();
    let ts = Local::now().format("%Y-%m-%d %H:%M:%S").to_string();

    if new.date.as_str() <= today { return changes; }

    if old.time != new.time {
        changes.push(CompanyEventChange {
            ticker: new.ticker.clone(),
            date: new.date.clone(),
            field_changed: "time".to_string(),
            old_value: old.time.clone(),
            new_value: new.time.clone(),
            detected_at: ts.clone(),
        });
    }

    if old.eps_forecast != new.eps_forecast {
        changes.push(CompanyEventChange {
            ticker: new.ticker.clone(),
            date: new.date.clone(),
            field_changed: "eps_forecast".to_string(),
            old_value: format!("{:?}", old.eps_forecast),
            new_value: format!("{:?}", new.eps_forecast),
            detected_at: ts.clone(),
        });
    }

    if old.eps_actual != new.eps_actual {
        changes.push(CompanyEventChange {
            ticker: new.ticker.clone(),
            date: new.date.clone(),
            field_changed: "eps_actual".to_string(),
            old_value: format!("{:?}", old.eps_actual),
            new_value: format!("{:?}", new.eps_actual),
            detected_at: ts.clone(),
        });
    }

    // Add similar for revenue if applicable

    changes
}

pub fn price_key(p: &CompanyPrice) -> String {
    if p.time.is_empty() {
        format!("{}|{}", p.ticker, p.date)
    } else {
        format!("{}|{}|{}", p.ticker, p.date, p.time)
    }
}

pub fn parse_float(s: &str) -> Option<f64> {
    s.replace("--", "").replace(",", "").parse::<f64>().ok()
}

pub fn parse_yahoo_date(s: &str) -> anyhow::Result<NaiveDate> {
    NaiveDate::parse_from_str(s, "%B %d, %Y")
        .or_else(|_| NaiveDate::parse_from_str(s, "%b %d, %Y"))
        .map_err(|_| anyhow::anyhow!("Bad date: {s}"))
}

/// Send-safe random range
pub fn random_range(min: u64, max: u64) -> u64 {
    let mut rng = StdRng::from_rng(&mut rand::rng());
    rng.random_range(min..max)
}

/// Send-safe random choice
pub fn choose_random<T: Clone>(items: &[T]) -> T {
    let mut rng = StdRng::from_rng(&mut rand::rng());
    items.choose(&mut rng).unwrap().clone()
}

/// Extract first valid Yahoo ticker from company
pub fn extract_first_yahoo_ticker(company: &CompanyCrossPlatformInfo) -> Option<String> {
    for tickers in company.isin_tickers_map.values() {
        for ticker in tickers {
            if ticker.starts_with("YAHOO:") 
                && ticker != "YAHOO:NO_RESULTS" 
                && ticker != "YAHOO:ERROR" 
            {
                return Some(ticker.trim_start_matches("YAHOO:").to_string());
            }
        }
    }
    None
}

/// Sanitize company name for file system use
pub fn sanitize_company_name(name: &str) -> String {
    name.replace("/", "_")
        .replace("\\", "_")
        .replace(":", "_")
        .replace("*", "_")
        .replace("?", "_")
        .replace("\"", "_")
        .replace("<", "_")
        .replace(">", "_")
        .replace("|", "_")
}

/// Load companies from JSONL file
pub async fn load_companies_from_jsonl(
    path: &std::path::Path
) -> anyhow::Result<Vec<CompanyCrossPlatformInfo>> {
    let content = tokio::fs::read_to_string(path).await?;
    let mut companies = Vec::new();
    
    for line in content.lines() {
        if line.trim().is_empty() {
            continue;
        }
        if let Ok(company) = serde_json::from_str::<CompanyCrossPlatformInfo>(line) {
            companies.push(company);
        }
    }
    
    Ok(companies)
}