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

use super::{AttendeeDocument, AttendeeDocumentType, AttendeeName};

/// Dados do participante do evento
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Attendee {
    /// Nome do participante (opcional)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub name: Option<AttendeeName>,

    /// Documento do participante (obrigatório)
    pub document: AttendeeDocument,

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

impl Attendee {
    /// Cria um builder para construir um participante
    pub fn builder() -> AttendeeBuilder {
        AttendeeBuilder::default()
    }
}

/// Builder para criar participantes
#[derive(Default)]
pub struct AttendeeBuilder {
    name: Option<AttendeeName>,
    document: Option<AttendeeDocument>,
    document_type: Option<AttendeeDocumentType>,
}

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

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

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

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

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

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

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

    #[test]
    fn test_attendee_complete() {
        let attendee = Attendee::builder()
            .name_str("João Silva")
            .unwrap()
            .document_str("12345678900")
            .unwrap()
            .document_type(AttendeeDocumentType::Cpf)
            .build()
            .unwrap();

        assert_eq!(attendee.name.as_ref().unwrap().as_str(), "João Silva");
        assert_eq!(attendee.document.as_str(), "12345678900");
        assert_eq!(attendee.document_type, Some(AttendeeDocumentType::Cpf));
    }

    #[test]
    fn test_attendee_minimal() {
        let attendee = Attendee::builder()
            .document_str("12345678900")
            .unwrap()
            .build()
            .unwrap();

        assert_eq!(attendee.document.as_str(), "12345678900");
        assert!(attendee.name.is_none());
        assert!(attendee.document_type.is_none());
    }

    #[test]
    fn test_attendee_missing_document() {
        let result = Attendee::builder().name_str("Test").unwrap().build();
        assert!(result.is_err());
    }

    #[test]
    fn test_attendee_serialization() {
        let attendee = Attendee::builder()
            .name_str("Test")
            .unwrap()
            .document_str("123")
            .unwrap()
            .document_type(AttendeeDocumentType::Passport)
            .build()
            .unwrap();

        let json = serde_json::to_string(&attendee).unwrap();
        assert!(json.contains("Test"));
        assert!(json.contains("123"));
        assert!(json.contains("passport"));
    }
}

