use crate::types::validation_errors::ValidationError;
use serde::{Deserialize, Serialize};
use std::fmt;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[repr(transparent)]
pub struct Installments(u16);

impl Installments {
    const MIN: u16 = 1;
    const MAX: u16 = 999;

    pub fn new(value: u16) -> Result<Self, ValidationError> {
        if !(Self::MIN..=Self::MAX).contains(&value) {
            return Err(ValidationError::InvalidFormat {
                field: "installments".to_string(),
                message: format!(
                    "installments must be between {} and {}, received: {}",
                    Self::MIN,
                    Self::MAX,
                    value
                ),
            });
        }

        Ok(Self(value))
    }

    pub fn value(&self) -> u16 {
        self.0
    }

    /// Verifica se é à vista (1 parcela)
    pub fn is_cash(&self) -> bool {
        self.0 == 1
    }

    pub fn is_installment(&self) -> bool {
        self.0 > 1
    }
}

impl fmt::Display for Installments {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        if self.0 == 1 {
            write!(f, "à vista")
        } else {
            write!(f, "{}x", self.0)
        }
    }
}

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

    #[test]
    fn test_installments_valid() {
        let installments = Installments::new(2).unwrap();
        assert_eq!(installments.value(), 2);
    }

    #[test]
    fn test_installments_cash() {
        let installments = Installments::new(1).unwrap();
        assert!(installments.is_cash());
        assert!(!installments.is_installment());
    }

    #[test]
    fn test_installments_multiple() {
        let installments = Installments::new(12).unwrap();
        assert!(!installments.is_cash());
        assert!(installments.is_installment());
    }

    #[test]
    fn test_installments_min() {
        let installments = Installments::new(1).unwrap();
        assert_eq!(installments.value(), 1);
    }

    #[test]
    fn test_installments_max() {
        let installments = Installments::new(999).unwrap();
        assert_eq!(installments.value(), 999);
    }

    #[test]
    fn test_installments_zero() {
        let result = Installments::new(0);
        assert!(result.is_err());
    }

    #[test]
    fn test_installments_too_high() {
        let result = Installments::new(1000);
        assert!(result.is_err());
    }

    #[test]
    fn test_installments_display() {
        let cash = Installments::new(1).unwrap();
        assert_eq!(cash.to_string(), "à vista");

        let installment = Installments::new(3).unwrap();
        assert_eq!(installment.to_string(), "3x");
    }
}

