use super::{Passenger, TravelLeg, TravelType};
use crate::types::shared::date_iso_8601::DateIso8601;
use crate::types::validation_errors::ValidationError;
use serde::{Deserialize, Serialize};

/// Dados de viagem
///
/// Ref: <https://docs.konduto.com/reference/travel>
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Travel {
    /// Tipo de viagem (flight ou bus)
    #[serde(rename = "type")]
    pub travel_type: TravelType,

    /// Data de expiração (formato ISO 8601 datetime)
    #[serde(skip_serializing_if = "Option::is_none")]
    pub expiration_date: Option<DateIso8601>,

    /// Informações da viagem de ida
    pub departure: TravelLeg,

    /// Informações da viagem de volta
    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(rename = "return")]
    pub return_leg: Option<TravelLeg>,

    /// Lista de passageiros
    pub passengers: Vec<Passenger>,
}

impl Travel {
    /// Cria um builder para construir dados de viagem
    pub fn builder() -> TravelBuilder {
        TravelBuilder::default()
    }
}

/// Builder para criar dados de viagem
#[derive(Default)]
pub struct TravelBuilder {
    travel_type: Option<TravelType>,
    expiration_date: Option<DateIso8601>,
    departure: Option<TravelLeg>,
    return_leg: Option<TravelLeg>,
    passengers: Vec<Passenger>,
}

impl TravelBuilder {
    pub fn travel_type(mut self, travel_type: TravelType) -> Self {
        self.travel_type = Some(travel_type);
        self
    }

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

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

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

    pub fn add_passenger(mut self, passenger: Passenger) -> Self {
        self.passengers.push(passenger);
        self
    }

    pub fn passengers(mut self, passengers: Vec<Passenger>) -> Self {
        self.passengers = passengers;
        self
    }

    pub fn build(self) -> Result<Travel, ValidationError> {
        let travel_type = self
            .travel_type
            .ok_or_else(|| ValidationError::EmptyField("travel_type".to_string()))?;

        let departure = self
            .departure
            .ok_or_else(|| ValidationError::EmptyField("departure".to_string()))?;

        if self.passengers.is_empty() {
            return Err(ValidationError::InvalidFormat {
                field: "passengers".to_string(),
                message: "at least one passenger is required".to_string(),
            });
        }

        // Validar campos obrigatórios baseados no tipo de viagem
        match travel_type {
            TravelType::Flight => {
                if departure.origin_airport.is_none() {
                    return Err(ValidationError::EmptyField("origin_airport".to_string()));
                }
                if departure.destination_airport.is_none() {
                    return Err(ValidationError::EmptyField(
                        "destination_airport".to_string(),
                    ));
                }
            }
            TravelType::Bus => {
                if departure.origin_city.is_none() {
                    return Err(ValidationError::EmptyField("origin_city".to_string()));
                }
                if departure.destination_city.is_none() {
                    return Err(ValidationError::EmptyField("destination_city".to_string()));
                }
            }
        }

        Ok(Travel {
            travel_type,
            expiration_date: self.expiration_date,
            departure,
            return_leg: self.return_leg,
            passengers: self.passengers,
        })
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::types::travel::TravelDocumentType;

    #[test]
    fn test_travel_flight() {
        let departure = TravelLeg::builder()
            .origin_airport_str("GRU")
            .unwrap()
            .destination_airport_str("JFK")
            .unwrap()
            .date("2018-12-25T18:00Z")
            .class_str("economy")
            .unwrap()
            .number_of_connections(1)
            .build()
            .unwrap();

        let passenger = Passenger::builder()
            .name_str("Júlia da Silva")
            .unwrap()
            .document_str("12345678909")
            .unwrap()
            .document_type(TravelDocumentType::Id)
            .build()
            .unwrap();

        let travel = Travel::builder()
            .travel_type(TravelType::Flight)
            .departure(departure)
            .add_passenger(passenger)
            .build()
            .unwrap();

        assert_eq!(travel.travel_type, TravelType::Flight);
        assert_eq!(travel.passengers.len(), 1);
    }

    #[test]
    fn test_travel_bus() {
        let departure = TravelLeg::builder()
            .origin_city_str("Santos")
            .unwrap()
            .destination_city_str("São Paulo")
            .unwrap()
            .date("2018-12-25T18:00Z")
            .build()
            .unwrap();

        let passenger = Passenger::builder()
            .name_str("João")
            .unwrap()
            .document_str("123")
            .unwrap()
            .document_type(TravelDocumentType::Id)
            .build()
            .unwrap();

        let travel = Travel::builder()
            .travel_type(TravelType::Bus)
            .departure(departure)
            .add_passenger(passenger)
            .build()
            .unwrap();

        assert_eq!(travel.travel_type, TravelType::Bus);
    }

    #[test]
    fn test_travel_with_return() {
        let departure = TravelLeg::builder()
            .origin_airport_str("GRU")
            .unwrap()
            .destination_airport_str("JFK")
            .unwrap()
            .date("2018-12-25T18:00Z")
            .build()
            .unwrap();

        let return_leg = TravelLeg::builder()
            .origin_airport_str("JFK")
            .unwrap()
            .destination_airport_str("GRU")
            .unwrap()
            .date("2018-12-30T18:00Z")
            .build()
            .unwrap();

        let passenger = Passenger::builder()
            .name_str("Maria")
            .unwrap()
            .document_str("456")
            .unwrap()
            .document_type(TravelDocumentType::Passport)
            .build()
            .unwrap();

        let travel = Travel::builder()
            .travel_type(TravelType::Flight)
            .departure(departure)
            .return_leg(return_leg)
            .add_passenger(passenger)
            .build()
            .unwrap();

        assert!(travel.return_leg.is_some());
    }

    #[test]
    fn test_travel_multiple_passengers() {
        let departure = TravelLeg::builder()
            .origin_airport_str("GRU")
            .unwrap()
            .destination_airport_str("JFK")
            .unwrap()
            .date("2018-12-25T18:00Z")
            .build()
            .unwrap();

        let passenger1 = Passenger::builder()
            .name_str("Júlia")
            .unwrap()
            .document_str("123")
            .unwrap()
            .document_type(TravelDocumentType::Id)
            .build()
            .unwrap();

        let passenger2 = Passenger::builder()
            .name_str("Carlos")
            .unwrap()
            .document_str("XYZ1234")
            .unwrap()
            .document_type(TravelDocumentType::Passport)
            .build()
            .unwrap();

        let travel = Travel::builder()
            .travel_type(TravelType::Flight)
            .departure(departure)
            .add_passenger(passenger1)
            .add_passenger(passenger2)
            .build()
            .unwrap();

        assert_eq!(travel.passengers.len(), 2);
    }

    #[test]
    fn test_travel_no_passengers() {
        let departure = TravelLeg::builder()
            .origin_airport_str("GRU")
            .unwrap()
            .destination_airport_str("JFK")
            .unwrap()
            .date("2018-12-25T18:00Z")
            .build()
            .unwrap();

        let result = Travel::builder()
            .travel_type(TravelType::Flight)
            .departure(departure)
            .build();

        assert!(result.is_err());
    }

    #[test]
    fn test_travel_flight_missing_airport() {
        let departure = TravelLeg::builder()
            .origin_city_str("São Paulo")
            .unwrap()
            .destination_city_str("Rio")
            .unwrap()
            .date("2018-12-25T18:00Z")
            .build()
            .unwrap();

        let passenger = Passenger::builder()
            .name_str("João")
            .unwrap()
            .document_str("123")
            .unwrap()
            .document_type(TravelDocumentType::Id)
            .build()
            .unwrap();

        let result = Travel::builder()
            .travel_type(TravelType::Flight)
            .departure(departure)
            .add_passenger(passenger)
            .build();

        assert!(result.is_err());
    }

    #[test]
    fn test_travel_bus_missing_city() {
        let departure = TravelLeg::builder()
            .origin_airport_str("GRU")
            .unwrap()
            .destination_airport_str("JFK")
            .unwrap()
            .date("2018-12-25T18:00Z")
            .build()
            .unwrap();

        let passenger = Passenger::builder()
            .name_str("João")
            .unwrap()
            .document_str("123")
            .unwrap()
            .document_type(TravelDocumentType::Id)
            .build()
            .unwrap();

        let result = Travel::builder()
            .travel_type(TravelType::Bus)
            .departure(departure)
            .add_passenger(passenger)
            .build();

        assert!(result.is_err());
    }

    #[test]
    fn test_travel_serialization() {
        let departure = TravelLeg::builder()
            .origin_airport_str("GRU")
            .unwrap()
            .destination_airport_str("JFK")
            .unwrap()
            .date("2018-12-25T18:00Z")
            .build()
            .unwrap();

        let passenger = Passenger::builder()
            .name_str("Test")
            .unwrap()
            .document_str("123")
            .unwrap()
            .document_type(TravelDocumentType::Id)
            .build()
            .unwrap();

        let travel = Travel::builder()
            .travel_type(TravelType::Flight)
            .departure(departure)
            .add_passenger(passenger)
            .build()
            .unwrap();

        let json = serde_json::to_string(&travel).unwrap();
        assert!(json.contains("flight"));
        assert!(json.contains("GRU"));
    }
}
