use crate::{
    datafile::datafile::DataFile,
    targeting::{
        conditions::{
            browser_condition::BrowserCondition, conversion_condition::ConversionCondition,
            cookie_condition::CookieCondition, custom_datum::CustomDatum, device_condition::DeviceCondition,
            exclusive_experiment_condition::ExclusiveExperimentCondition, geolocation_condition::GeolocationCondition,
            kcs_heat_range_condition::KcsHeatRangeCondition, operating_system_condition::OperatingSystemCondition,
            page_title_condition::PageTitleCondition, page_url_condition::PageUrlCondition,
            page_view_number_condition::PageViewNumberCondition, previous_page_condition::PreviousPageCondition,
            sdk_language_condition::SdkLanguageCondition, segment_condition::SegmentCondition,
            target_experiment_condition::TargetExperimentCondition,
            target_feature_flag_condition::TargetFeatureFlagCondition,
            target_personalization_condition::TargetPersonalizationCondition,
            time_elapsed_since_visit_condition::TimeElapsedSinceVisitCondition, version_condition::VersionCondition,
            visit_number_today_condition::VisitNumberTodayCondition,
            visit_number_total_condition::VisitNumberTotalCondition, visitor_code_condition::VisitorCodeCondition,
            visitor_new_return_condition::VisitorNewReturnCondition,
        },
        models::{ConditionModel, ConditionParamsModel},
        TargetingData,
    },
};

pub mod browser_condition;
pub mod conversion_condition;
pub mod cookie_condition;
pub mod custom_datum;
pub mod device_condition;
pub mod exclusive_experiment_condition;
pub mod geolocation_condition;
pub mod kcs_heat_range_condition;
pub mod number_condition;
pub mod operating_system_condition;
pub mod page_title_condition;
pub mod page_url_condition;
pub mod page_view_number_condition;
pub mod previous_page_condition;
pub mod sdk_language_condition;
pub mod segment_condition;
pub mod string_value_condition;
pub mod target_experiment_condition;
pub mod target_feature_flag_condition;
pub mod target_personalization_condition;
pub mod time_elapsed_since_visit_condition;
pub mod unknown_condition;
pub mod version_condition;
pub mod visit_number_today_condition;
pub mod visit_number_total_condition;
pub mod visitor_code_condition;
pub mod visitor_new_return_condition;
pub(crate) mod visitor_scope_condition;

pub(crate) trait TargetingCondition: Send + Sync {
    fn check(&self, data: &TargetingData<impl DataFile>) -> Option<bool>;
}

pub(crate) enum TarCond {
    CustomDatum(CustomDatum),
    TargetFeatureFlag(TargetFeatureFlagCondition),
    TargetExperiment(TargetExperimentCondition),
    TargetPersonalization(TargetPersonalizationCondition),
    ExclusiveExperiment(ExclusiveExperimentCondition),
    Browser(BrowserCondition),
    DeviceType(DeviceCondition),
    VisitorCode(VisitorCodeCondition),
    SdkLanguage(SdkLanguageCondition),
    PageTitle(PageTitleCondition),
    PageUrl(PageUrlCondition),
    PageViews(PageViewNumberCondition),
    PreviousPage(PreviousPageCondition),
    Conversions(ConversionCondition),
    Cookie(CookieCondition),
    OperatingSystem(OperatingSystemCondition),
    Geolocation(GeolocationCondition),
    Segment(SegmentCondition),
    FirstVisit(TimeElapsedSinceVisitCondition),
    LastVisit(TimeElapsedSinceVisitCondition),
    Visits(VisitNumberTotalCondition),
    SameDayVisits(VisitNumberTodayCondition),
    NewVisitors(VisitorNewReturnCondition),
    HeatSlice(KcsHeatRangeCondition),
    ApplicationVersion(VersionCondition),
    Unknown,
}

impl TargetingCondition for TarCond {
    fn check(&self, data: &TargetingData<impl DataFile>) -> Option<bool> {
        match self {
            Self::CustomDatum(c) => c.check(data),
            Self::TargetFeatureFlag(c) => c.check(data),
            Self::TargetExperiment(c) => c.check(data),
            Self::TargetPersonalization(c) => c.check(data),
            Self::ExclusiveExperiment(c) => c.check(data),
            Self::Browser(c) => c.check(data),
            Self::DeviceType(c) => c.check(data),
            Self::VisitorCode(c) => c.check(data),
            Self::SdkLanguage(c) => c.check(data),
            Self::PageTitle(c) => c.check(data),
            Self::PageUrl(c) => c.check(data),
            Self::PageViews(c) => c.check(data),
            Self::PreviousPage(c) => c.check(data),
            Self::Conversions(c) => c.check(data),
            Self::Cookie(c) => c.check(data),
            Self::OperatingSystem(c) => c.check(data),
            Self::Geolocation(c) => c.check(data),
            Self::Segment(c) => c.check(data),
            Self::FirstVisit(c) => c.check(data),
            Self::LastVisit(c) => c.check(data),
            Self::Visits(c) => c.check(data),
            Self::SameDayVisits(c) => c.check(data),
            Self::NewVisitors(c) => c.check(data),
            Self::HeatSlice(c) => c.check(data),
            Self::ApplicationVersion(c) => c.check(data),
            Self::Unknown => Some(true),
        }
    }
}

impl TarCond {
    pub fn from_json(model: ConditionModel) -> Option<Self> {
        type CP = ConditionParamsModel;
        let tar_cond = match model.params {
            CP::Browser(m) => Self::Browser(BrowserCondition::from_json(m)?),
            CP::Conversions(m) => Self::Conversions(ConversionCondition::from_json(m)),
            CP::Cookie(m) => Self::Cookie(CookieCondition::from_json(m)?),
            CP::CustomDatum(m) => Self::CustomDatum(CustomDatum::from_json(m)?),
            CP::DeviceType(m) => Self::DeviceType(DeviceCondition::from_json(m)?),
            CP::ExclusiveExperiment(m) => Self::ExclusiveExperiment(ExclusiveExperimentCondition::from_json(m)?),
            CP::Geolocation(m) => Self::Geolocation(GeolocationCondition::from_json(m)?),
            CP::HeatSlice(m) => Self::HeatSlice(KcsHeatRangeCondition::from_json(m)?),
            CP::NewVisitors(m) => Self::NewVisitors(VisitorNewReturnCondition::from_json(m)?),
            CP::OperatingSystem(m) => Self::OperatingSystem(OperatingSystemCondition::from_json(m)?),
            CP::PageTitle(m) => Self::PageTitle(PageTitleCondition::from_json(m)?),
            CP::PageUrl(m) => Self::PageUrl(PageUrlCondition::from_json(m)?),
            CP::PageViews(m) => Self::PageViews(PageViewNumberCondition::from_json(m)?),
            CP::PreviousPage(m) => Self::PreviousPage(PreviousPageCondition::from_json(m)?),
            CP::SameDayVisits(m) => Self::SameDayVisits(VisitNumberTodayCondition::from_json(m)?),
            CP::SdkLanguage(m) => Self::SdkLanguage(SdkLanguageCondition::from_json(m)?),
            CP::Segment(m) => Self::Segment(SegmentCondition::from_json(m)?),
            CP::FirstVisit(m) => Self::FirstVisit(TimeElapsedSinceVisitCondition::from_json(true, m)?),
            CP::LastVisit(m) => Self::LastVisit(TimeElapsedSinceVisitCondition::from_json(false, m)?),
            CP::TargetExperiment(m) => Self::TargetExperiment(TargetExperimentCondition::from_json(m)?),
            CP::TargetFeatureFlag(m) => Self::TargetFeatureFlag(TargetFeatureFlagCondition::from_json(m)?),
            CP::TargetPersonalization(m) => Self::TargetPersonalization(TargetPersonalizationCondition::from_json(m)?),
            CP::VisitorCode(m) => Self::VisitorCode(VisitorCodeCondition::from_json(m)?),
            CP::Visits(m) => Self::Visits(VisitNumberTotalCondition::from_json(m)?),
            CP::ApplicationVersion(m) => Self::ApplicationVersion(VersionCondition::from_json(m)?),
            CP::Unknown => Self::Unknown,
        };
        return Some(tar_cond);
    }

    #[cfg(test)]
    pub fn kind(&self) -> &'static str {
        match self {
            Self::CustomDatum(_) => "CUSTOM_DATUM",
            Self::TargetFeatureFlag(_) => "TARGET_FEATURE_FLAG",
            Self::TargetExperiment(_) => "TARGET_EXPERIMENT",
            Self::TargetPersonalization(_) => "TARGET_PERSONALIZATION",
            Self::ExclusiveExperiment(_) => "EXCLUSIVE_EXPERIMENT",
            Self::Browser(_) => "BROWSER",
            Self::DeviceType(_) => "DEVICE_TYPE",
            Self::VisitorCode(_) => "VISITOR_CODE",
            Self::SdkLanguage(_) => "SDK_LANGUAGE",
            Self::PageTitle(_) => "PAGE_TITLE",
            Self::PageUrl(_) => "PAGE_URL",
            Self::PageViews(_) => "PAGE_VIEWS",
            Self::PreviousPage(_) => "PREVIOUS_PAGE",
            Self::Conversions(_) => "CONVERSIONS",
            Self::Cookie(_) => "COOKIE",
            Self::OperatingSystem(_) => "OPERATING_SYSTEM",
            Self::Geolocation(_) => "GEOLOCATION",
            Self::Segment(_) => "SEGMENT",
            Self::FirstVisit(_) => "FIRST_VISIT",
            Self::LastVisit(_) => "LAST_VISIT",
            Self::Visits(_) => "VISITS",
            Self::SameDayVisits(_) => "SAME_DAY_VISITS",
            Self::NewVisitors(_) => "NEW_VISITORS",
            Self::HeatSlice(_) => "HEAT_SLICE",
            Self::ApplicationVersion(_) => "APPLICATION_VERSION",
            Self::Unknown => "?",
        }
    }
}
