use crate::targeting::conditions::unknown_condition::UnknownCondition;
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>;
}
macro_rules! define_tar_cond {
    (
        $(($variant:ident, $type:ty, $kind:literal, $constructor:expr)),* $(,)?
    ) => {
        pub(crate) enum TarCond {
            $($variant($type),)*
        }

        impl TargetingCondition for TarCond {
            fn check(&self, data: &TargetingData<impl DataFile>) -> Option<bool> {
                match self {
                    $(Self::$variant(c) => c.check(data),)*
                }
            }
        }

        impl TarCond {
            pub fn from_json(model: ConditionModel) -> Option<Self> {
                Some(match model.params {
                    $(ConditionParamsModel::$variant(m) => Self::$variant($constructor(m)?),)*
                })
            }

            #[cfg(test)]
            pub fn kind(&self) -> &'static str {
                match self {
                    $(Self::$variant(_) => $kind,)*
                }
            }
        }
    };
}

define_tar_cond!(
    (CustomDatum, CustomDatum, "CUSTOM_DATUM", CustomDatum::from_json),
    (TargetFeatureFlag, TargetFeatureFlagCondition, "TARGET_FEATURE_FLAG", TargetFeatureFlagCondition::from_json),
    (TargetExperiment, TargetExperimentCondition, "TARGET_EXPERIMENT", TargetExperimentCondition::from_json),
    (
        TargetPersonalization,
        TargetPersonalizationCondition,
        "TARGET_PERSONALIZATION",
        TargetPersonalizationCondition::from_json
    ),
    (
        ExclusiveExperiment,
        ExclusiveExperimentCondition,
        "EXCLUSIVE_EXPERIMENT",
        ExclusiveExperimentCondition::from_json
    ),
    (Browser, BrowserCondition, "BROWSER", BrowserCondition::from_json),
    (DeviceType, DeviceCondition, "DEVICE_TYPE", DeviceCondition::from_json),
    (VisitorCode, VisitorCodeCondition, "VISITOR_CODE", VisitorCodeCondition::from_json),
    (SdkLanguage, SdkLanguageCondition, "SDK_LANGUAGE", SdkLanguageCondition::from_json),
    (PageTitle, PageTitleCondition, "PAGE_TITLE", PageTitleCondition::from_json),
    (PageUrl, PageUrlCondition, "PAGE_URL", PageUrlCondition::from_json),
    (PageViews, PageViewNumberCondition, "PAGE_VIEWS", PageViewNumberCondition::from_json),
    (PreviousPage, PreviousPageCondition, "PREVIOUS_PAGE", PreviousPageCondition::from_json),
    (Conversions, ConversionCondition, "CONVERSIONS", ConversionCondition::from_json),
    (Cookie, CookieCondition, "COOKIE", CookieCondition::from_json),
    (OperatingSystem, OperatingSystemCondition, "OPERATING_SYSTEM", OperatingSystemCondition::from_json),
    (Geolocation, GeolocationCondition, "GEOLOCATION", GeolocationCondition::from_json),
    (Segment, SegmentCondition, "SEGMENT", SegmentCondition::from_json),
    (FirstVisit, TimeElapsedSinceVisitCondition, "FIRST_VISIT", |m| TimeElapsedSinceVisitCondition::from_json(true, m)),
    (LastVisit, TimeElapsedSinceVisitCondition, "LAST_VISIT", |m| TimeElapsedSinceVisitCondition::from_json(false, m)),
    (Visits, VisitNumberTotalCondition, "VISITS", VisitNumberTotalCondition::from_json),
    (SameDayVisits, VisitNumberTodayCondition, "SAME_DAY_VISITS", VisitNumberTodayCondition::from_json),
    (NewVisitors, VisitorNewReturnCondition, "NEW_VISITORS", VisitorNewReturnCondition::from_json),
    (HeatSlice, KcsHeatRangeCondition, "HEAT_SLICE", KcsHeatRangeCondition::from_json),
    (ApplicationVersion, VersionCondition, "APPLICATION_VERSION", VersionCondition::from_json),
    (Unknown, UnknownCondition, "?", UnknownCondition::from_json),
);
