//! Agent log writer — creates JSONL log files and appends agent events.
use crate::agents::AgentEvent;
use chrono::Utc;
use serde::{Deserialize, Serialize};
use std::fs::{self, File, OpenOptions};
use std::io::Write;
use std::path::{Path, PathBuf};

/// A single line in the agent log file (JSONL format).
#[derive(Debug, Serialize, Deserialize)]
pub struct LogEntry {
    pub timestamp: String,
    #[serde(flatten)]
    pub event: serde_json::Value,
}

/// Writes agent events to a persistent log file (JSONL format).
///
/// Each agent session gets its own log file at:
///   `.huskies/logs/{story_id}/{agent_name}-{session_id}.log`
pub struct AgentLogWriter {
    file: File,
}

impl AgentLogWriter {
    /// Create a new log writer, creating the directory structure as needed.
    ///
    /// The log file is opened in append mode so that a restart mid-session
    /// won't overwrite earlier output.
    pub fn new(
        project_root: &Path,
        story_id: &str,
        agent_name: &str,
        session_id: &str,
    ) -> Result<Self, String> {
        let dir = log_dir(project_root, story_id);
        fs::create_dir_all(&dir)
            .map_err(|e| format!("Failed to create log directory {}: {e}", dir.display()))?;

        let path = dir.join(format!("{agent_name}-{session_id}.log"));
        let file = OpenOptions::new()
            .create(true)
            .append(true)
            .open(&path)
            .map_err(|e| format!("Failed to open log file {}: {e}", path.display()))?;

        Ok(Self { file })
    }

    /// Write an agent event as a JSONL line with an ISO 8601 timestamp.
    pub fn write_event(&mut self, event: &AgentEvent) -> Result<(), String> {
        let event_value =
            serde_json::to_value(event).map_err(|e| format!("Failed to serialize event: {e}"))?;

        let entry = LogEntry {
            timestamp: Utc::now().to_rfc3339(),
            event: event_value,
        };

        let mut line =
            serde_json::to_string(&entry).map_err(|e| format!("Failed to serialize entry: {e}"))?;
        line.push('\n');

        self.file
            .write_all(line.as_bytes())
            .map_err(|e| format!("Failed to write log entry: {e}"))?;

        Ok(())
    }
}

/// Return the log directory for a story.
pub(super) fn log_dir(project_root: &Path, story_id: &str) -> PathBuf {
    project_root.join(".huskies").join("logs").join(story_id)
}

/// Return the path to a specific log file.
#[allow(dead_code)]
pub fn log_file_path(
    project_root: &Path,
    story_id: &str,
    agent_name: &str,
    session_id: &str,
) -> PathBuf {
    log_dir(project_root, story_id).join(format!("{agent_name}-{session_id}.log"))
}
