use std::path::PathBuf;
use tokio::fs;

use crate::types::OutputKind;

/// Manages output files for a single task
pub struct TaskOutputManager {
    base_dir: PathBuf,
}

impl TaskOutputManager {
    pub fn new(base_dir: PathBuf) -> Self {
        Self { base_dir }
    }

    pub async fn ensure_dir(&self) -> std::io::Result<()> {
        fs::create_dir_all(&self.base_dir).await
    }

    pub fn stdout_path(&self) -> PathBuf {
        self.base_dir.join("stdout.log")
    }
    pub fn stderr_path(&self) -> PathBuf {
        self.base_dir.join("stderr.log")
    }
    pub fn combined_path(&self) -> PathBuf {
        self.base_dir.join("combined.log")
    }

    /// Rotate existing files (.log -> .log.1, .log.1 -> .log.2, etc.)
    pub async fn rotate(&self) -> std::io::Result<()> {
        for name in &["stdout.log", "stderr.log", "combined.log"] {
            let path = self.base_dir.join(name);
            if fs::try_exists(&path).await.unwrap_or(false) {
                let mut n = 1;
                loop {
                    let rotated = self.base_dir.join(format!("{}.{}", name, n));
                    if !fs::try_exists(&rotated).await.unwrap_or(false) {
                        fs::rename(&path, &rotated).await?;
                        break;
                    }
                    n += 1;
                }
            }
        }
        Ok(())
    }

    /// Read output file contents (tail N lines if specified)
    pub async fn read_output(
        &self,
        kind: OutputKind,
        tail_lines: Option<usize>,
    ) -> std::io::Result<String> {
        let path = match kind {
            OutputKind::Stdout => self.stdout_path(),
            OutputKind::Stderr => self.stderr_path(),
            OutputKind::Combined => self.combined_path(),
        };

        if !fs::try_exists(&path).await.unwrap_or(false) {
            return Ok(String::new());
        }

        let content = fs::read_to_string(&path).await?;

        if let Some(n) = tail_lines {
            let lines: Vec<&str> = content.lines().collect();
            let start = lines.len().saturating_sub(n);
            Ok(lines[start..].join("\n"))
        } else {
            Ok(content)
        }
    }

    pub async fn clear(&self) -> std::io::Result<()> {
        for path in &[self.stdout_path(), self.stderr_path(), self.combined_path()] {
            if fs::try_exists(path).await.unwrap_or(false) {
                fs::write(path, b"").await?;
            }
        }
        Ok(())
    }
}
