use serde::{Deserialize, Serialize};

/// Represents the current state of a job in its lifecycle
#[derive(Debug, Clone, Copy, Serialize, Deserialize, Eq, PartialEq)]
#[repr(i32)]
pub enum JobStatus {
    /// Job is waiting to be picked up by a worker
    Queued = 0,
    /// Job is currently being processed
    Running = 1,
    /// Job completed successfully
    Finished = 2,
    /// Job failed during execution
    Failed = 3,
    /// Job was canceled before completion
    Canceled = 4,
    /// Invalid or unknown status
    Invalid = -1,
}

impl Default for JobStatus {
    fn default() -> Self {
        Self::Queued
    }
}

impl JobStatus {
    /// Convert JobStatus to i32 for database storage
    pub fn to_i32(self) -> i32 {
        self as i32
    }

    /// Convert i32 from database to JobStatus
    pub fn from_i32(value: i32) -> Self {
        match value {
            0 => Self::Queued,
            1 => Self::Running,
            2 => Self::Finished,
            3 => Self::Failed,
            4 => Self::Canceled,
            -1 => Self::Invalid,
            _ => Self::Invalid, // Default fallback
        }
    }
}

impl TryFrom<i32> for JobStatus {
    type Error = String;

    fn try_from(value: i32) -> Result<Self, Self::Error> {
        Ok(Self::from_i32(value))
    }
}

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

    #[test]
    fn test_default_is_queued() {
        // Arrange & Act
        let status = JobStatus::default();

        // Assert
        assert_eq!(status, JobStatus::Queued);
    }

    #[test]
    fn test_to_i32_conversion() {
        // Arrange & Act & Assert
        assert_eq!(JobStatus::Queued.to_i32(), 0);
        assert_eq!(JobStatus::Running.to_i32(), 1);
        assert_eq!(JobStatus::Finished.to_i32(), 2);
        assert_eq!(JobStatus::Failed.to_i32(), 3);
        assert_eq!(JobStatus::Canceled.to_i32(), 4);
        assert_eq!(JobStatus::Invalid.to_i32(), -1);
    }

    #[test]
    fn test_from_i32_valid_values() {
        // Arrange & Act & Assert
        assert_eq!(JobStatus::from_i32(0), JobStatus::Queued);
        assert_eq!(JobStatus::from_i32(1), JobStatus::Running);
        assert_eq!(JobStatus::from_i32(2), JobStatus::Finished);
        assert_eq!(JobStatus::from_i32(3), JobStatus::Failed);
        assert_eq!(JobStatus::from_i32(4), JobStatus::Canceled);
        assert_eq!(JobStatus::from_i32(-1), JobStatus::Invalid);
    }

    #[test]
    fn test_from_i32_invalid_value_defaults_to_invalid() {
        // Arrange
        let invalid_values = [5, 10, 100];

        // Act & Assert
        for value in invalid_values {
            assert_eq!(JobStatus::from_i32(value), JobStatus::Invalid);
        }
    }

    #[test]
    fn test_try_from_i32() {
        // Arrange & Act
        let result = JobStatus::try_from(2);

        // Assert
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), JobStatus::Finished);
    }

    #[test]
    fn test_round_trip_conversion() {
        // Arrange
        let statuses = [
            JobStatus::Queued,
            JobStatus::Running,
            JobStatus::Finished,
            JobStatus::Failed,
            JobStatus::Canceled,
            JobStatus::Invalid,
        ];

        // Act & Assert
        for status in statuses {
            let i32_value = status.to_i32();
            let converted = JobStatus::from_i32(i32_value);
            assert_eq!(status, converted);
        }
    }
}