use crate::network::dto::ConfigurationDto;

pub const REQUIRED_CONSENT_TYPE: &str = "REQUIRED";
const FULLY_BLOCK_CONSENT_BEHAVIOR: &str = "FULLY_BLOCK";

#[derive(Debug, Clone, PartialEq)]
enum ConsentType {
    Required,
    NotRequired,
}

impl From<&String> for ConsentType {
    fn from(s: &String) -> Self {
        match s.to_ascii_uppercase().as_str() {
            REQUIRED_CONSENT_TYPE => ConsentType::Required,
            _ => ConsentType::NotRequired,
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq)]
pub(crate) enum ConsentBlockingBehaviour {
    PartiallyBlocked,
    CompletelyBlocked,
}

impl From<&str> for ConsentBlockingBehaviour {
    fn from(s: &str) -> Self {
        match s.to_ascii_uppercase().as_str() {
            FULLY_BLOCK_CONSENT_BEHAVIOR => ConsentBlockingBehaviour::CompletelyBlocked,
            _ => ConsentBlockingBehaviour::PartiallyBlocked,
        }
    }
}

#[cfg_attr(test, mockall::automock)]
pub(crate) trait Settings {
    fn consent_required(&self) -> bool;
    fn blocking_behaviour(&self) -> ConsentBlockingBehaviour;
    fn is_streaming_mode(&self) -> bool;
    fn data_api_domain(&self) -> &str;
    fn streaming_domain(&self) -> &str;
}

#[derive(Debug)]
pub(crate) struct SettingsImpl {
    configuration_dto: ConfigurationDto,
    consent_type: ConsentType,
    blocking_behaviour: ConsentBlockingBehaviour,
}

impl Settings for SettingsImpl {
    fn data_api_domain(&self) -> &str {
        &self.configuration_dto.data_api_domain
    }

    fn streaming_domain(&self) -> &str {
        &self.configuration_dto.streaming_api_domain
    }

    fn consent_required(&self) -> bool {
        self.consent_type == ConsentType::Required
    }

    fn blocking_behaviour(&self) -> ConsentBlockingBehaviour {
        self.blocking_behaviour
    }

    fn is_streaming_mode(&self) -> bool {
        self.configuration_dto.real_time_update
    }
}

impl SettingsImpl {
    pub fn new(configuration_dto: ConfigurationDto) -> Self {
        Self {
            consent_type: (&configuration_dto.consent_type).into(),
            blocking_behaviour: ConsentBlockingBehaviour::from(configuration_dto.consent_opt_out_behavior.as_str()),
            configuration_dto,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::datafile::datafile_test_utils::make_configuration_dto;
    use rstest::rstest;

    #[rstest]
    #[case("REQUIRED", true)]
    #[case("required", true)]
    #[case("NotRequired", false)]
    #[case("", false)]
    fn test_consent_required(#[case] consent_str: &str, #[case] expected: bool) {
        let dto = ConfigurationDto {
            data_api_domain: "data.example.com".to_string(),
            streaming_api_domain: "stream.example.com".to_string(),
            consent_type: consent_str.to_owned(),
            consent_opt_out_behavior: "".to_owned(),
            real_time_update: false,
        };
        let settings = SettingsImpl::new(dto);
        assert_eq!(settings.consent_required(), expected);
    }

    #[rstest]
    #[case("FULLY_BLOCK", ConsentBlockingBehaviour::CompletelyBlocked)]
    #[case("fully_block", ConsentBlockingBehaviour::CompletelyBlocked)]
    #[case("PARTIALLY_BLOCK", ConsentBlockingBehaviour::PartiallyBlocked)]
    #[case("", ConsentBlockingBehaviour::PartiallyBlocked)]
    #[case("invalid", ConsentBlockingBehaviour::PartiallyBlocked)]
    fn test_blocking_behaviour_if_consent_not_given(
        #[case] consent_opt_out_behavior: &str,
        #[case] expected: ConsentBlockingBehaviour,
    ) {
        let mut dto = make_configuration_dto(true, false, "data.example.com", "stream.example.com");
        dto.consent_opt_out_behavior = consent_opt_out_behavior.to_owned();
        let settings = SettingsImpl::new(dto);
        assert_eq!(settings.blocking_behaviour(), expected);
    }

    #[rstest]
    #[case(true)]
    #[case(false)]
    fn test_is_streaming_mode(#[case] real_time: bool) {
        let dto = make_configuration_dto(true, real_time, "data.example.com", "stream.example.com");
        let settings = SettingsImpl::new(dto);
        assert_eq!(settings.is_streaming_mode(), real_time);
    }

    #[rstest]
    #[case("data.example.com", "stream.example.com")]
    #[case("api.localhost", "stream.localhost")]
    fn test_domains(#[case] data_api: &str, #[case] streaming_api: &str) {
        let dto = make_configuration_dto(true, true, data_api, streaming_api);
        let settings = SettingsImpl::new(dto);
        assert_eq!(settings.data_api_domain(), data_api);
        assert_eq!(settings.streaming_domain(), streaming_api);
    }
}
