//! Error types for filter graph operations.

use thiserror::Error;

/// Errors that can occur during filter graph construction and processing.
#[derive(Debug, Error)]
pub enum FilterError {
    /// Failed to build the filter graph (invalid filter chain or `FFmpeg` error
    /// during graph creation).
    #[error("failed to build filter graph")]
    BuildFailed,

    /// A frame processing operation (push or pull) failed.
    #[error("failed to process frame")]
    ProcessFailed,

    /// An invalid configuration was detected during graph construction.
    #[error("invalid filter configuration: {reason}")]
    InvalidConfig {
        /// Human-readable reason for the failure.
        reason: String,
    },

    /// A frame was pushed to an invalid input slot.
    #[error("invalid input: slot={slot} reason={reason}")]
    InvalidInput {
        /// The slot index that was out of range or otherwise invalid.
        slot: usize,
        /// Human-readable reason for the failure.
        reason: String,
    },

    /// An underlying `FFmpeg` function returned an error code.
    #[error("ffmpeg error: {message} (code={code})")]
    Ffmpeg {
        /// The raw `FFmpeg` error code.
        code: i32,
        /// Human-readable description of the error.
        message: String,
    },
}

#[cfg(test)]
mod tests {
    use super::FilterError;
    use std::error::Error;

    #[test]
    fn build_failed_should_display_correct_message() {
        let err = FilterError::BuildFailed;
        assert_eq!(err.to_string(), "failed to build filter graph");
    }

    #[test]
    fn process_failed_should_display_correct_message() {
        let err = FilterError::ProcessFailed;
        assert_eq!(err.to_string(), "failed to process frame");
    }

    #[test]
    fn invalid_input_should_display_slot_and_reason() {
        let err = FilterError::InvalidInput {
            slot: 2,
            reason: "slot out of range".to_string(),
        };
        assert_eq!(
            err.to_string(),
            "invalid input: slot=2 reason=slot out of range"
        );
    }

    #[test]
    fn ffmpeg_should_display_code_and_message() {
        let err = FilterError::Ffmpeg {
            code: -22,
            message: "Invalid argument".to_string(),
        };
        assert_eq!(err.to_string(), "ffmpeg error: Invalid argument (code=-22)");
    }

    #[test]
    fn filter_error_should_implement_std_error() {
        fn assert_error<E: Error>(_: &E) {}
        assert_error(&FilterError::BuildFailed);
        assert_error(&FilterError::ProcessFailed);
        assert_error(&FilterError::InvalidInput {
            slot: 0,
            reason: String::new(),
        });
        assert_error(&FilterError::Ffmpeg {
            code: 0,
            message: String::new(),
        });
    }
}
