use std::time::Duration;

use serde::Deserialize;
use serde_with::{DurationSeconds, serde_as};

use crate::targeting::models::SegmentModel;

#[cfg(test)]
use serde::Serialize;

// MARK: - Access Token (JWT)
#[serde_as]
#[derive(Deserialize)]
#[cfg_attr(test, derive(Serialize))]
pub(crate) struct Jwt {
    pub access_token: String,
    #[serde_as(as = "DurationSeconds<u64>")]
    pub expires_in: Duration,
}

// MARK: - Data File

#[derive(Debug, Deserialize)]
pub(crate) struct DataFileDto {
    pub configuration: ConfigurationDto,
    #[serde(rename = "featureFlags")]
    pub feature_flags: Vec<FeatureFlagDto>,
    #[serde(rename = "customData")]
    pub custom_data: Vec<CustomDataDto>,
    pub segments: Vec<SegmentModel>,
    #[serde(rename = "dateModified", default)]
    pub date_modified: u64,
    pub holdout: Option<ExperimentDto>,
}

#[derive(Debug, Deserialize)]
pub(crate) struct ConfigurationDto {
    #[serde(rename = "realTimeUpdate")]
    pub real_time_update: bool,
    #[serde(rename = "consentType")]
    pub consent_type: String,
    #[serde(rename = "dataApiDomain")]
    pub data_api_domain: String,
    #[serde(rename = "streamingApiDomain")]
    pub streaming_api_domain: String,
}

#[cfg(test)]
impl Default for ConfigurationDto {
    fn default() -> Self {
        Self {
            real_time_update: false,
            consent_type: "NOT_REQUIRED".to_owned(),
            data_api_domain: "".to_owned(),
            streaming_api_domain: "".to_owned(),
        }
    }
}

#[derive(Debug, Deserialize)]
#[cfg_attr(test, derive(Clone))]
pub(crate) struct FeatureFlagDto {
    pub id: u32,
    #[serde(rename = "featureKey")]
    pub feature_key: String,
    #[serde(rename = "environmentEnabled")]
    pub environment_enabled: bool,
    pub variations: Vec<VariationDto>,
    #[serde(rename = "defaultVariationKey")]
    pub default_variation_key: String,
    pub rules: Vec<RuleDto>,

    // Optional fields
    #[serde(rename = "mutuallyExclusiveGroup")]
    pub mutually_exclusive_group: Option<String>,
    #[serde(rename = "bucketingCustomDataId")]
    pub bucketing_custom_data_id: Option<u32>,
}

#[derive(Debug, Deserialize)]
#[cfg_attr(test, derive(Clone))]
pub(crate) struct VariationDto {
    pub key: String,
    #[serde(default)]
    pub name: String,
    pub variables: Vec<VariableDto>,
}

#[derive(Debug, Deserialize)]
#[cfg_attr(test, derive(Clone))]
pub(crate) struct VariableDto {
    pub key: String,
    #[serde(rename = "type")]
    pub var_type: String,
    pub value: serde_json::Value, // can be string, number, bool, or JSON string
}

#[derive(Debug, Deserialize)]
#[cfg_attr(test, derive(Clone))]
pub(crate) struct RuleDto {
    // pub order: u32,
    pub id: u32,
    #[serde(rename = "type")]
    pub rule_type: String,
    #[serde(rename = "segmentId")]
    pub segment_id: Option<u32>,
    pub exposition: f64,
    #[serde(rename = "respoolTime")]
    pub respool_time: Option<u64>,
    #[serde(flatten)]
    pub experiment: ExperimentDto,
}

#[derive(Debug, Deserialize)]
#[cfg_attr(test, derive(Clone))]
pub(crate) struct ExperimentDto {
    #[serde(rename = "experimentId")]
    pub id: u32,
    #[serde(rename = "variationByExposition")]
    pub variation_by_exposition: Vec<VariationByExpositionDto>,
}

#[derive(Debug, Deserialize)]
#[cfg_attr(test, derive(Clone))]
pub(crate) struct VariationByExpositionDto {
    #[serde(rename = "variationKey")]
    pub variation_key: String,
    #[serde(rename = "variationId")]
    pub variation_id: u32,
    pub exposition: f64,
}

#[derive(Debug, Deserialize)]
#[cfg_attr(test, derive(Clone))]
pub(crate) struct CustomDataDto {
    pub id: u32,
    pub index: u32,
    pub name: String,
    #[serde(rename = "localOnly")]
    pub local_only: bool,
    #[serde(rename = "isMappingIdentifier")]
    pub is_mapping_identifier: bool,
    pub scope: String,
}
