use std::{fmt::Debug, sync::Arc};

use crate::network::dto::VariationByExpositionDto;

#[cfg_attr(test, mockall::automock)]
pub(crate) trait VariationByExposition: Send + Sync + Debug {
    fn variation_key(&self) -> &Arc<str>;
    fn variation_id(&self) -> u32;
    fn exposition(&self) -> f64;
}

#[derive(Debug, Clone)]
#[cfg_attr(test, derive(PartialEq))]
pub(crate) struct VariationByExpositionImpl {
    variation_key: Arc<str>,
    variation_id: u32,
    exposition: f64,
}

impl VariationByExposition for VariationByExpositionImpl {
    fn variation_key(&self) -> &Arc<str> {
        &self.variation_key
    }

    fn variation_id(&self) -> u32 {
        self.variation_id
    }

    fn exposition(&self) -> f64 {
        self.exposition
    }
}

impl From<VariationByExpositionDto> for VariationByExpositionImpl {
    fn from(dto: VariationByExpositionDto) -> Self {
        Self::new(dto)
    }
}

impl VariationByExpositionImpl {
    pub fn new(dto: VariationByExpositionDto) -> Self {
        Self {
            variation_key: Arc::from(dto.variation_key),
            variation_id: dto.variation_id,
            exposition: dto.exposition,
        }
    }
}

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

    #[rstest]
    #[case("A", 123, 0.75)]
    #[case("B", 0, 0.5)]
    fn test_variation_exposition_impl_new(#[case] key: &str, #[case] variation_id: u32, #[case] exposition: f64) {
        // Arrange
        let dto = make_var_exp_dto(key, variation_id, exposition);
        let exposition_impl = VariationByExpositionImpl::new(dto);

        // Assert
        assert_eq!(key, exposition_impl.variation_key().as_ref());
        assert_eq!(variation_id, exposition_impl.variation_id());
        assert_eq!(exposition, exposition_impl.exposition());
    }

    #[rstest]
    #[case("A", 123, 0.75)]
    #[case("B", 0, 0.5)]
    fn test_variation_exposition_impl_from(#[case] key: &str, #[case] variation_id: u32, #[case] exposition: f64) {
        // Arrange
        let dto = make_var_exp_dto(key, variation_id, exposition);
        let exposition_impl: VariationByExpositionImpl = dto.into();

        // Assert
        assert_eq!(key, exposition_impl.variation_key().as_ref());
        assert_eq!(variation_id, exposition_impl.variation_id());
        assert_eq!(exposition, exposition_impl.exposition());
    }
}
