use std::path::PathBuf;

use serde::{Deserialize, Serialize};
use uuid::Uuid;

#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(tag = "type", content = "payload", rename_all = "snake_case")]
pub enum LogAvailability {
    Headless {
        stdout_path: PathBuf,
        stderr_path: PathBuf,
    },
    TmuxPaneSnapshot,
    Unavailable {
        reason: LogsUnavailableReason,
    },
}

#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum LogsUnavailableReason {
    TmuxTarget,
    CaptureDisabled,
    PaneUnavailable,
    PipeInUse,
    RecorderFailed,
}

impl LogsUnavailableReason {
    pub const fn as_str(self) -> &'static str {
        match self {
            Self::TmuxTarget => "tmux_target",
            Self::CaptureDisabled => "capture_disabled",
            Self::PaneUnavailable => "pane_unavailable",
            Self::PipeInUse => "pipe_in_use",
            Self::RecorderFailed => "recorder_failed",
        }
    }
}

#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct CaptureRequest {
    pub session_id: Uuid,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub scrollback_lines: Option<u32>,
}

#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct PaneSnapshot {
    pub content: String,
    pub captured_at_ms: u64,
    pub scrollback_lines_requested: u32,
    pub scrollback_lines_included: u32,
    pub pane_history_lines: u32,
}

#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[non_exhaustive]
#[serde(tag = "type", content = "payload", rename_all = "snake_case")]
pub enum CaptureError {
    NotATmuxTarget,
    PaneUnavailable,
    SessionMissing,
    TmuxNotAvailable,
    CapturePaneFailed { stderr: String },
}

#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[non_exhaustive]
#[serde(tag = "status", content = "payload", rename_all = "snake_case")]
pub enum CaptureResponse {
    Captured(PaneSnapshot),
    Failed(CaptureError),
}

impl CaptureResponse {
    pub fn into_result(self) -> Result<PaneSnapshot, CaptureError> {
        match self {
            Self::Captured(snapshot) => Ok(snapshot),
            Self::Failed(error) => Err(error),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::LogsUnavailableReason;

    #[test]
    fn logs_unavailable_reason_as_str_matches_serde_snake_case() {
        assert_eq!(LogsUnavailableReason::TmuxTarget.as_str(), "tmux_target");
        assert_eq!(
            LogsUnavailableReason::CaptureDisabled.as_str(),
            "capture_disabled"
        );
        assert_eq!(
            LogsUnavailableReason::PaneUnavailable.as_str(),
            "pane_unavailable"
        );
        assert_eq!(LogsUnavailableReason::PipeInUse.as_str(), "pipe_in_use");
        assert_eq!(
            LogsUnavailableReason::RecorderFailed.as_str(),
            "recorder_failed"
        );
    }
}
