use super::{GuestDocument, GuestDocumentType, GuestName};
use crate::types::address::Country;
use crate::types::shared::date_iso_8601::DateIso8601;
use crate::types::validation_errors::ValidationError;
use serde::{Deserialize, Serialize};

/// Dados do hóspede
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct HotelGuest {
    /// Nome do hóspede (obrigatório)
    pub name: GuestName,

    /// Número do documento (recomendado)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub document: Option<GuestDocument>,

    /// Tipo do documento (recomendado)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub document_type: Option<GuestDocumentType>,

    /// Data de nascimento (recomendado, formato ISO 8601: YYYY-MM-DD)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub dob: Option<DateIso8601>,

    /// Nacionalidade (recomendado, ISO 3166-2)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub nationality: Option<Country>,
}

impl HotelGuest {
    /// Cria um builder para construir um hóspede
    pub fn builder() -> HotelGuestBuilder {
        HotelGuestBuilder::default()
    }
}

/// Builder para criar hóspedes
#[derive(Default)]
pub struct HotelGuestBuilder {
    name: Option<GuestName>,
    document: Option<GuestDocument>,
    document_type: Option<GuestDocumentType>,
    dob: Option<DateIso8601>,
    nationality: Option<Country>,
}

impl HotelGuestBuilder {
    pub fn name(mut self, name: GuestName) -> Self {
        self.name = Some(name);
        self
    }

    pub fn name_str(mut self, name: impl Into<String>) -> Result<Self, ValidationError> {
        self.name = Some(GuestName::try_new(name)?);
        Ok(self)
    }

    pub fn document(mut self, document: GuestDocument) -> Self {
        self.document = Some(document);
        self
    }

    pub fn document_str(mut self, document: impl Into<String>) -> Result<Self, ValidationError> {
        self.document = Some(GuestDocument::try_new(document)?);
        Ok(self)
    }

    pub fn document_type(mut self, doc_type: GuestDocumentType) -> Self {
        self.document_type = Some(doc_type);
        self
    }

    pub fn dob(mut self, dob: DateIso8601) -> Self {
        self.dob = Some(dob);
        self
    }

    pub fn dob_str(mut self, dob: impl Into<String>) -> Result<Self, ValidationError> {
        self.dob = Some(DateIso8601::new(dob)?);
        Ok(self)
    }

    pub fn nationality(mut self, nationality: Country) -> Self {
        self.nationality = Some(nationality);
        self
    }

    pub fn nationality_code(mut self, code: impl Into<String>) -> Result<Self, ValidationError> {
        self.nationality = Some(Country::from_code(code)?);
        Ok(self)
    }

    pub fn build(self) -> Result<HotelGuest, ValidationError> {
        Ok(HotelGuest {
            name: self
                .name
                .ok_or_else(|| ValidationError::EmptyField("guest_name".to_string()))?,
            document: self.document,
            document_type: self.document_type,
            dob: self.dob,
            nationality: self.nationality,
        })
    }
}

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

    #[test]
    fn test_hotel_guest_complete() {
        let guest = HotelGuest::builder()
            .name_str("Mary Jane")
            .unwrap()
            .document_str("6253107")
            .unwrap()
            .document_type(GuestDocumentType::Id)
            .dob_str("1970-01-01")
            .unwrap()
            .nationality(Country::BR)
            .build()
            .unwrap();

        assert_eq!(guest.name.as_str(), "Mary Jane");
        assert_eq!(guest.document.as_ref().unwrap().as_str(), "6253107");
        assert_eq!(guest.document_type, Some(GuestDocumentType::Id));
    }

    #[test]
    fn test_hotel_guest_minimal() {
        let guest = HotelGuest::builder()
            .name_str("João Silva")
            .unwrap()
            .build()
            .unwrap();

        assert_eq!(guest.name.as_str(), "João Silva");
        assert!(guest.document.is_none());
        assert!(guest.nationality.is_none());
    }

    #[test]
    fn test_hotel_guest_missing_name() {
        let result = HotelGuest::builder().build();
        assert!(result.is_err());
    }

    #[test]
    fn test_hotel_guest_serialization() {
        let guest = HotelGuest::builder()
            .name_str("John Smith")
            .unwrap()
            .document_str("XYZ1234")
            .unwrap()
            .document_type(GuestDocumentType::Passport)
            .nationality(Country::US)
            .build()
            .unwrap();

        let json = serde_json::to_string(&guest).unwrap();
        assert!(json.contains("John Smith"));
        assert!(json.contains("XYZ1234"));
        assert!(json.contains("passport"));
    }
}

