use std::sync::atomic::{AtomicBool, Ordering};
use std::{path::Path, sync::Arc};

use image::ImageReader;

use crate::error::FsPulseError;

use super::validator::{ValidationState, Validator};

/// Validator implementation for FLAC audio files using the Claxon crate.
pub struct ImageValidator;

impl ImageValidator {
    /// Constructs a new ImageValidator instance.
    pub fn new() -> Self {
        ImageValidator
    }
}

impl Validator for ImageValidator {
    fn validate(
        &self,
        path: &Path,
        interrupt_token: &Arc<AtomicBool>,
    ) -> Result<(ValidationState, Option<String>), FsPulseError> {

        // Check for interrupt before starting. Because of how this validator works,
        // this is the only test we can do
        if interrupt_token.load(Ordering::Acquire) {
            return Err(FsPulseError::ScanInterrupted);
        }

        let open_result = ImageReader::open(path);
        let reader = match open_result {
            Ok(reader) => reader,
            Err(e) => {
                let e_str = e.to_string();
                return Ok((ValidationState::Invalid, Some(e_str)));
            }
        };

        match reader.decode() {
            Ok(_) => Ok((ValidationState::Valid, None)),
            Err(e) => {
                let e_str = e.to_string();
                Ok((ValidationState::Invalid, Some(e_str)))
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::path::Path;
    use std::sync::Arc;

    #[test]
    fn test_image_validator_nonexistent_file() {
        let validator = ImageValidator::new();
        let nonexistent_path = Path::new("/this/path/does/not/exist.jpg");
        let interrupt_token = Arc::new(AtomicBool::new(false));

        let result = validator.validate(nonexistent_path, &interrupt_token);
        assert!(result.is_ok());
        
        let (state, error_msg) = result.unwrap();
        assert_eq!(state, ValidationState::Invalid);
        assert!(error_msg.is_some());
        let msg = error_msg.unwrap();
        assert!(
            msg.contains("No such file or directory") || 
            msg.contains("cannot find the file") ||
            msg.contains("system cannot find the file") ||
            msg.contains("The system cannot find the file") ||
            msg.contains("The system cannot find the path specified") ||
            msg.contains("Access is denied") ||
            msg.to_lowercase().contains("not found") ||
            msg.to_lowercase().contains("no such file"),
            "Unexpected error message for nonexistent file: {msg}"
        );
    }

    #[test]
    fn test_image_validator_invalid_file() {
        let validator = ImageValidator::new();

        // Create a temporary file with invalid image content
        use std::io::Write;
        use tempfile::NamedTempFile;

        let mut temp_file = NamedTempFile::new().expect("Failed to create temp file");
        temp_file.write_all(b"not an image file").expect("Failed to write temp file");

        let interrupt_token = Arc::new(AtomicBool::new(false));
        let result = validator.validate(temp_file.path(), &interrupt_token);
        assert!(result.is_ok());
        
        let (state, error_msg) = result.unwrap();
        assert_eq!(state, ValidationState::Invalid);
        assert!(error_msg.is_some());
    }

    #[test]
    fn test_image_validator_empty_file() {
        let validator = ImageValidator::new();

        // Create a temporary empty file
        use tempfile::NamedTempFile;

        let temp_file = NamedTempFile::new().expect("Failed to create temp file");

        let interrupt_token = Arc::new(AtomicBool::new(false));
        let result = validator.validate(temp_file.path(), &interrupt_token);
        assert!(result.is_ok());
        
        let (state, error_msg) = result.unwrap();
        assert_eq!(state, ValidationState::Invalid);
        assert!(error_msg.is_some());
    }
}
