//! JSONL line parser for Claude Code session files.

use std::io::BufRead;

use camino::Utf8Path;

use crate::error::Result;
use crate::types::RawMessage;

/// Parse a single JSONL line into a RawMessage.
/// Returns None for lines that cannot be parsed (logged via tracing).
pub fn parse_line(line: &str, line_number: usize) -> Option<RawMessage> {
    match serde_json::from_str::<RawMessage>(line) {
        Ok(msg) => Some(msg),
        Err(e) => {
            tracing::warn!(line = line_number, error = %e, "Skipping unparseable JSONL line");
            None
        }
    }
}

/// Parse all messages from a JSONL file.
/// Skips unparseable lines (lenient). Returns I/O errors.
pub fn parse_session(path: &Utf8Path) -> Result<Vec<RawMessage>> {
    let file = std::fs::File::open(path.as_std_path())?;
    let reader = std::io::BufReader::new(file);
    let mut messages = Vec::new();

    for (i, line) in reader.lines().enumerate() {
        let line = line?;
        if line.trim().is_empty() {
            continue;
        }
        if let Some(msg) = parse_line(&line, i + 1) {
            messages.push(msg);
        }
    }

    Ok(messages)
}

/// Parse a session JSONL file with streaming deduplication applied.
///
/// Claude Code writes multiple JSONL lines for the same assistant message as
/// it streams. This function collapses those into a single final version per
/// uuid. See [`crate::dedup::dedup_messages`] for details.
pub fn parse_session_deduped(path: &Utf8Path) -> Result<Vec<RawMessage>> {
    let messages = parse_session(path)?;
    Ok(crate::dedup::dedup_messages(messages))
}
