// src/corporate/checkpoint_helpers.rs
//! Shared helpers for checkpoint-based recovery and logging
//! 
//! This module extracts common patterns used across multiple update modules
//! to reduce code duplication and improve maintainability.

use super::types::CompanyCrossPlatformInfo;
use crate::util::logger;
use std::collections::HashMap;
use std::path::{Path};
use tokio::fs::{File};
use tokio::io::{AsyncWriteExt};
use anyhow::Result;

/// Load companies from checkpoint and replay log for recovery
/// 
/// This function implements the checkpoint + write-ahead log pattern:
/// 1. Loads the main checkpoint file
/// 2. Replays any pending updates from the log file
/// 3. Returns the merged state
pub async fn load_checkpoint_with_log<P1, P2>(
    checkpoint_path: P1,
    log_path: P2,
    checkpoint_desc: &str,
) -> Result<HashMap<String, CompanyCrossPlatformInfo>> 
where
    P1: AsRef<Path>,
    P2: AsRef<Path>,
{
    let checkpoint_path = checkpoint_path.as_ref();
    let log_path = log_path.as_ref();
    
    let mut companies: HashMap<String, CompanyCrossPlatformInfo> = HashMap::new();
    
    // Load checkpoint if it exists
    if checkpoint_path.exists() {
        logger::log_info(&format!("Loading checkpoint from {}...", checkpoint_desc)).await;
        let content = tokio::fs::read_to_string(checkpoint_path).await?;
        
        for line in content.lines() {
            if line.trim().is_empty() || !line.ends_with('}') {
                continue; // Skip incomplete lines
            }
            
            match serde_json::from_str::<CompanyCrossPlatformInfo>(line) {
                Ok(company) => {
                    companies.insert(company.name.clone(), company);
                }
                Err(e) => {
                    logger::log_warn(&format!("Skipping invalid checkpoint line: {}", e)).await;
                }
            }
        }
        logger::log_info(&format!("Loaded checkpoint with {} companies", companies.len())).await;
    }
    
    // Replay log if it exists
    if log_path.exists() {
        logger::log_info("Replaying update log...").await;
        let log_content = tokio::fs::read_to_string(log_path).await?;
        let mut replayed = 0;
        
        for line in log_content.lines() {
            if line.trim().is_empty() || !line.ends_with('}') {
                continue; // Skip incomplete lines
            }
            
            match serde_json::from_str::<CompanyCrossPlatformInfo>(line) {
                Ok(company) => {
                    companies.insert(company.name.clone(), company);
                    replayed += 1;
                }
                Err(e) => {
                    logger::log_warn(&format!("Skipping invalid log line: {}", e)).await;
                }
            }
        }
        
        if replayed > 0 {
            logger::log_info(&format!("Replayed {} updates from log", replayed)).await;
        }
    }
    
    Ok(companies)
}

/// Consolidate log into checkpoint and clear log
/// 
/// Atomically writes all companies to a new checkpoint file and removes the log.
/// Uses atomic rename to ensure crash safety.
pub async fn consolidate_checkpoint<P1, P2>(
    checkpoint_path: P1,
    log_path: P2,
    companies: &HashMap<String, CompanyCrossPlatformInfo>,
) -> Result<()>
where
    P1: AsRef<Path>,
    P2: AsRef<Path>,
{
    let checkpoint_path = checkpoint_path.as_ref();
    let log_path = log_path.as_ref();
    
    logger::log_info("Consolidating update log into checkpoint...").await;
    
    let temp_checkpoint = checkpoint_path.with_extension("tmp");
    let mut temp_file = File::create(&temp_checkpoint).await?;
    
    for company in companies.values() {
        let json_line = serde_json::to_string(company)?;
        temp_file.write_all(json_line.as_bytes()).await?;
        temp_file.write_all(b"\n").await?;
    }
    
    temp_file.flush().await?;
    temp_file.sync_data().await?;
    drop(temp_file);
    
    tokio::fs::rename(&temp_checkpoint, checkpoint_path).await?;
    
    // Remove log after successful consolidation
    if log_path.exists() {
        tokio::fs::remove_file(log_path).await.ok();
    }
    
    logger::log_info(&format!("✓ Consolidated {} companies", companies.len())).await;
    
    Ok(())
}

/// Check if log file has content
pub async fn log_has_content<P: AsRef<Path>>(log_path: P) -> bool {
    if let Ok(metadata) = tokio::fs::metadata(log_path.as_ref()).await {
        metadata.len() > 0
    } else {
        false
    }
}

/// Load enrichment progress from log file
/// 
/// Used by enrichment functions to track which companies have already been processed.
/// Parses log entries with format: {"company_name": "...", "status": "enriched", ...}
pub async fn load_enrichment_progress<P>(
    log_path: P,
) -> Result<std::collections::HashSet<String>>
where
    P: AsRef<Path>,
{
    let mut enriched_companies = std::collections::HashSet::new();
    
    if !log_path.as_ref().exists() {
        return Ok(enriched_companies);
    }
    
    logger::log_info("Loading enrichment progress from log...").await;
    let log_content = tokio::fs::read_to_string(log_path.as_ref()).await?;
    
    for line in log_content.lines() {
        if line.trim().is_empty() || !line.ends_with('}') {
            continue; // Skip incomplete lines
        }
        
        match serde_json::from_str::<serde_json::Value>(line) {
            Ok(entry) => {
                if let Some(name) = entry.get("company_name").and_then(|v| v.as_str()) {
                    if entry.get("status").and_then(|v| v.as_str()) == Some("enriched") {
                        enriched_companies.insert(name.to_string());
                    }
                }
            }
            Err(e) => {
                logger::log_warn(&format!("Skipping invalid log line: {}", e)).await;
            }
        }
    }
    
    logger::log_info(&format!(
        "Loaded {} enriched companies from log",
        enriched_companies.len()
    )).await;
    
    Ok(enriched_companies)
}

/// Count enriched companies by checking for data files
/// 
/// Walks through the corporate directory and counts companies that have
/// a data file in the specified subdirectory (e.g., "events", "options", "chart").
pub async fn count_enriched_companies(
    paths: &crate::util::directories::DataPaths,
    data_type: &str,
) -> Result<usize> {
    let corporate_dir = paths.corporate_dir();
    
    if !corporate_dir.exists() {
        return Ok(0);
    }
    
    let mut count = 0;
    let mut entries = tokio::fs::read_dir(&corporate_dir).await?;
    
    while let Some(entry) = entries.next_entry().await? {
        let path = entry.path();
        if path.is_dir() {
            let data_dir = path.join(data_type);
            let data_file = data_dir.join("data.jsonl");
            
            if data_file.exists() {
                count += 1;
            }
        }
    }
    
    Ok(count)
}