//! Agent log reader — reads, scans, and formats JSONL agent log files.
use super::format::format_log_entry_as_text;
use super::writer::{LogEntry, log_dir};
use std::fs;
use std::io::{BufRead, BufReader};
use std::path::{Path, PathBuf};

/// Read all log entries from a log file.
pub fn read_log(path: &Path) -> Result<Vec<LogEntry>, String> {
    let file = fs::File::open(path)
        .map_err(|e| format!("Failed to open log file {}: {e}", path.display()))?;
    let reader = BufReader::new(file);
    let mut entries = Vec::new();

    for line in reader.lines() {
        let line = line.map_err(|e| format!("Failed to read log line: {e}"))?;
        let trimmed = line.trim();
        if trimmed.is_empty() {
            continue;
        }
        let entry: LogEntry =
            serde_json::from_str(trimmed).map_err(|e| format!("Failed to parse log entry: {e}"))?;
        entries.push(entry);
    }

    Ok(entries)
}

/// List all log files for a story, optionally filtered by agent name prefix.
///
/// Returns files sorted by modification time (oldest first) so that when all
/// sessions are concatenated the timeline reads chronologically.
pub fn list_story_log_files(
    project_root: &Path,
    story_id: &str,
    agent_name_filter: Option<&str>,
) -> Vec<PathBuf> {
    let dir = log_dir(project_root, story_id);
    if !dir.is_dir() {
        return Vec::new();
    }

    let prefix = agent_name_filter.map(|n| format!("{n}-"));
    let mut files: Vec<(PathBuf, std::time::SystemTime)> = Vec::new();

    if let Ok(entries) = fs::read_dir(&dir) {
        for entry in entries.flatten() {
            let path = entry.path();
            let name = match path.file_name().and_then(|n| n.to_str()) {
                Some(n) => n.to_string(),
                None => continue,
            };
            if !name.ends_with(".log") {
                continue;
            }
            if let Some(ref pfx) = prefix
                && !name.starts_with(pfx.as_str())
            {
                continue;
            }
            let modified = entry
                .metadata()
                .and_then(|m| m.modified())
                .unwrap_or(std::time::SystemTime::UNIX_EPOCH);
            files.push((path, modified));
        }
    }

    files.sort_by_key(|(_, t)| *t);
    files.into_iter().map(|(p, _)| p).collect()
}

/// Read log entries from a file and convert them to human-readable text lines,
/// stripping raw streaming noise and JSON internals.
pub fn read_log_as_readable_lines(path: &Path) -> Result<Vec<String>, String> {
    let entries = read_log(path)?;
    let mut result = Vec::new();
    for entry in &entries {
        if let Some(line) = format_log_entry_as_text(&entry.timestamp, &entry.event) {
            result.push(line);
        }
    }
    Ok(result)
}

/// Find the most recent log file for a given story+agent combination.
///
/// Scans `.huskies/logs/{story_id}/` for files matching `{agent_name}-*.log`
/// and returns the one with the most recent modification time.
pub fn find_latest_log(project_root: &Path, story_id: &str, agent_name: &str) -> Option<PathBuf> {
    let dir = log_dir(project_root, story_id);
    if !dir.is_dir() {
        return None;
    }

    let prefix = format!("{agent_name}-");
    let mut best: Option<(PathBuf, std::time::SystemTime)> = None;

    let entries = fs::read_dir(&dir).ok()?;
    for entry in entries.flatten() {
        let path = entry.path();
        let name = match path.file_name().and_then(|n| n.to_str()) {
            Some(n) => n.to_string(),
            None => continue,
        };
        if !name.starts_with(&prefix) || !name.ends_with(".log") {
            continue;
        }
        let modified = match entry.metadata().and_then(|m| m.modified()) {
            Ok(t) => t,
            Err(_) => continue,
        };
        if best.as_ref().is_none_or(|(_, t)| modified > *t) {
            best = Some((path, modified));
        }
    }

    best.map(|(p, _)| p)
}
