//! Compiled-in pharmaceutical domain definition.
//!
//! This defines the complete pharma ontology with entity types, predicate schemas,
//! and source hierarchy. Using compiled-in definitions (vs TOML) provides:
//! - Type safety at compile time
//! - No runtime parsing errors
//! - IDE autocomplete for predicate names
//! - Version control for ontology changes

use crate::domain::{
    DefaultLens, Domain, EntityType, NamingConvention, PredicateSchema, SourceTier,
};
use stemedb_core::types::SourceClass;

/// GLP-1 agonist drugs with their brand name aliases.
///
/// Used for drug name normalization in subject construction.
pub const GLP1_DRUGS: &[DrugAlias] = &[
    DrugAlias { canonical: "Semaglutide", aliases: &["Ozempic", "Wegovy", "Rybelsus"] },
    DrugAlias { canonical: "Tirzepatide", aliases: &["Mounjaro", "Zepbound"] },
    DrugAlias { canonical: "Liraglutide", aliases: &["Victoza", "Saxenda"] },
    DrugAlias { canonical: "Dulaglutide", aliases: &["Trulicity"] },
    DrugAlias { canonical: "Exenatide", aliases: &["Byetta", "Bydureon"] },
];

/// A drug with its canonical name and brand aliases.
#[derive(Debug, Clone, Copy)]
pub struct DrugAlias {
    /// The canonical (generic) drug name.
    pub canonical: &'static str,
    /// Brand name aliases that map to the canonical name.
    pub aliases: &'static [&'static str],
}

/// Efficacy predicates for drug outcomes.
pub const PHARMA_EFFICACY_PREDICATES: &[&str] = &[
    "hba1c_reduction_percent",
    "hba1c_change_absolute",
    "weight_loss_percent",
    "weight_loss_kg",
    "fasting_glucose_reduction",
    "postprandial_glucose_reduction",
    "blood_pressure_reduction_systolic",
    "blood_pressure_reduction_diastolic",
    "ldl_cholesterol_reduction",
    "triglyceride_reduction",
    "cardiovascular_events_reduction",
    "major_adverse_cardiac_events_rate",
    "all_cause_mortality_reduction",
    "kidney_disease_progression_reduction",
    "response_rate",
    "remission_rate",
    "time_to_response_weeks",
];

/// Safety predicates for adverse events and warnings.
pub const PHARMA_SAFETY_PREDICATES: &[&str] = &[
    "has_boxed_warning",
    "boxed_warning_text",
    "adverse_event_rate",
    "nausea_rate",
    "vomiting_rate",
    "diarrhea_rate",
    "constipation_rate",
    "gastroparesis_risk",
    "pancreatitis_risk",
    "thyroid_c_cell_tumor_risk",
    "medullary_thyroid_carcinoma_risk",
    "retinopathy_risk",
    "gallbladder_disease_risk",
    "acute_kidney_injury_risk",
    "hypoglycemia_risk",
    "injection_site_reaction_rate",
    "discontinuation_rate_adverse_events",
    "serious_adverse_event_rate",
    "death_rate_treatment",
    "muscle_loss_observed",
    "lean_mass_preserved",
    "bone_density_change",
    "hair_loss_reported",
];

/// Mechanism predicates for drug pharmacology.
pub const PHARMA_MECHANISM_PREDICATES: &[&str] = &[
    "primary_target",
    "mechanism_of_action",
    "receptor_binding_affinity",
    "half_life_hours",
    "bioavailability_percent",
    "metabolism_pathway",
    "excretion_route",
    "drug_drug_interactions",
    "food_interactions",
    "onset_of_action_hours",
    "duration_of_action_hours",
    "peak_plasma_concentration_hours",
    "volume_of_distribution_liters",
    "protein_binding_percent",
];

/// Regulatory predicates for approval status.
pub const PHARMA_REGULATORY_PREDICATES: &[&str] = &[
    "fda_approval_date",
    "ema_approval_date",
    "approved_indications",
    "approved_dose_range",
    "max_approved_dose_mg",
    "approval_pathway",
    "orphan_drug_designation",
    "breakthrough_therapy_designation",
    "fast_track_designation",
    "accelerated_approval",
    "rems_required",
    "black_box_warning_added_date",
    "label_update_date",
];

/// Comparison predicates for head-to-head trials.
pub const PHARMA_COMPARISON_PREDICATES: &[&str] = &[
    "superiority_demonstrated",
    "non_inferiority_demonstrated",
    "relative_efficacy",
    "relative_safety",
    "cost_effectiveness_ratio",
    "quality_adjusted_life_years",
];

/// Build the pharmaceutical domain definition.
///
/// This is the canonical definition of the pharma vertical ontology.
/// All medical/pharma extractors should use this domain for subject construction.
pub fn definition() -> Domain {
    let mut domain = Domain::new(
        "Pharma",
        "Pharmaceutical and medical domain for drug efficacy, safety, and mechanisms",
    );

    // -------------------------------------------------------------------------
    // Entity Types
    // -------------------------------------------------------------------------

    // Drug entity with GLP-1 aliases
    let mut drug_entity = EntityType::required("A pharmaceutical compound or active ingredient")
        .with_naming(NamingConvention::CamelCase);

    for drug in GLP1_DRUGS {
        for alias in drug.aliases {
            drug_entity = drug_entity.with_alias(*alias, drug.canonical);
        }
    }

    domain = domain.with_entity_type("Drug", drug_entity);

    // Indication entity (medical conditions)
    domain = domain.with_entity_type(
        "Indication",
        EntityType::required("A medical condition or disease")
            .with_naming(NamingConvention::CamelCase)
            .with_alias("T2D", "Type2Diabetes")
            .with_alias("T2DM", "Type2Diabetes")
            .with_alias("Type 2 Diabetes", "Type2Diabetes")
            .with_alias("Obesity", "ChronicWeightManagement")
            .with_alias("CVD", "CardiovascularDisease")
            .with_alias("CKD", "ChronicKidneyDisease")
            .with_alias("NASH", "NonalcoholicSteatohepatitis")
            .with_alias("NAFLD", "NonalcoholicFattyLiverDisease"),
    );

    // Target entity (molecular targets)
    domain = domain.with_entity_type(
        "Target",
        EntityType::required("A molecular target (receptor, enzyme, pathway)")
            .with_naming(NamingConvention::CamelCase)
            .with_alias("GLP-1R", "GLP1R")
            .with_alias("GLP-1 Receptor", "GLP1R")
            .with_alias("GIP Receptor", "GIPR")
            .with_alias("Glucagon Receptor", "GCGR"),
    );

    // Comparator entity (for head-to-head trials)
    domain = domain.with_entity_type(
        "Comparator",
        EntityType::required("A comparator drug or placebo")
            .with_naming(NamingConvention::CamelCase)
            .with_alias("PBO", "Placebo"),
    );

    // Outcome entity (for specific outcomes)
    domain = domain.with_entity_type(
        "Outcome",
        EntityType::optional("A specific outcome measure").with_naming(NamingConvention::CamelCase),
    );

    // -------------------------------------------------------------------------
    // Predicate Schemas
    // -------------------------------------------------------------------------

    // Efficacy: Drug + Indication determine collision
    domain = domain.with_predicate_schema(
        "efficacy",
        PredicateSchema::new(
            "Efficacy predicates for drug outcomes in specific indications",
            "{Drug}:{Indication}",
        )
        .with_predicates(PHARMA_EFFICACY_PREDICATES.to_vec())
        .with_default_lens(DefaultLens::LayeredConsensus),
    );

    // Safety: Just Drug determines collision (applies across indications)
    domain = domain.with_predicate_schema(
        "safety",
        PredicateSchema::new("Safety predicates for adverse events and warnings", "{Drug}")
            .with_predicates(PHARMA_SAFETY_PREDICATES.to_vec())
            .with_default_lens(DefaultLens::Skeptic), // Safety should show all claims
    );

    // Mechanism: Drug + Target determines collision
    domain = domain.with_predicate_schema(
        "mechanism",
        PredicateSchema::new("Mechanism predicates for drug pharmacology", "{Drug}:{Target}")
            .with_predicates(PHARMA_MECHANISM_PREDICATES.to_vec())
            .with_default_lens(DefaultLens::Authority),
    );

    // Pharmacokinetics: Just Drug (target-independent properties)
    domain = domain.with_predicate_schema(
        "pharmacokinetics",
        PredicateSchema::new("Pharmacokinetic predicates (ADME properties)", "{Drug}")
            .with_predicates(vec![
                "half_life_hours",
                "bioavailability_percent",
                "volume_of_distribution_liters",
                "protein_binding_percent",
                "peak_plasma_concentration_hours",
            ])
            .with_default_lens(DefaultLens::Authority),
    );

    // Regulatory: Just Drug (approval status)
    domain = domain.with_predicate_schema(
        "regulatory",
        PredicateSchema::new("Regulatory predicates for approval status", "{Drug}")
            .with_predicates(PHARMA_REGULATORY_PREDICATES.to_vec())
            .with_default_lens(DefaultLens::Recency), // Most recent approval info
    );

    // Comparison: Drug + Comparator + Indication
    domain = domain.with_predicate_schema(
        "comparison",
        PredicateSchema::new(
            "Comparison predicates for head-to-head trials",
            "{Drug}:{Comparator}:{Indication}",
        )
        .with_predicates(PHARMA_COMPARISON_PREDICATES.to_vec())
        .with_default_lens(DefaultLens::LayeredConsensus),
    );

    // -------------------------------------------------------------------------
    // Source Hierarchy
    // -------------------------------------------------------------------------

    domain = domain.with_source_hierarchy(vec![
        SourceTier::new(SourceClass::Regulatory, "Tier 0: Regulatory Bodies")
            .with_examples(vec![
                "FDA Approval Letters",
                "FDA Drug Labels",
                "EMA Assessment Reports",
                "WHO Essential Medicines",
            ])
            .with_weight(1.0),
        SourceTier::new(SourceClass::Clinical, "Tier 1: Clinical Trials")
            .with_examples(vec![
                "Phase III RCTs",
                "Lancet Publications",
                "NEJM Publications",
                "Cochrane Reviews",
            ])
            .with_weight(0.9)
            .with_decay(730), // 2 years half-life
        SourceTier::new(SourceClass::Observational, "Tier 2: Observational Studies")
            .with_examples(vec![
                "Real-World Evidence",
                "Cohort Studies",
                "Case-Control Studies",
                "FAERS Reports (aggregated)",
            ])
            .with_weight(0.7)
            .with_decay(365), // 1 year half-life
        SourceTier::new(SourceClass::Expert, "Tier 3: Expert Opinion")
            .with_examples(vec![
                "Clinical Guidelines",
                "ADA Standards of Care",
                "Expert Consensus Statements",
                "Medical Textbooks",
            ])
            .with_weight(0.5)
            .with_decay(180), // 6 months half-life
        SourceTier::new(SourceClass::Community, "Tier 4: Curated Community")
            .with_examples(vec![
                "PatientsLikeMe",
                "Diabetes Forums (moderated)",
                "Healthcare Provider Discussions",
            ])
            .with_weight(0.3)
            .with_decay(90), // 3 months half-life
        SourceTier::new(SourceClass::Anecdotal, "Tier 5: Anecdotal Reports")
            .with_examples(vec![
                "Reddit r/Ozempic",
                "Twitter/X Posts",
                "Individual Testimonials",
                "Blog Posts",
            ])
            .with_weight(0.1)
            .with_decay(30), // 1 month half-life
    ]);

    domain
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_definition_builds() {
        let domain = definition();
        assert_eq!(domain.name, "Pharma");
        assert!(!domain.entity_types.is_empty());
        assert!(!domain.predicate_schemas.is_empty());
        assert!(!domain.source_hierarchy.is_empty());
    }

    #[test]
    fn test_drug_normalization() {
        let domain = definition();
        let drug_type = domain.get_entity_type("Drug").expect("Drug type");

        assert_eq!(drug_type.normalize("Ozempic"), "Semaglutide");
        assert_eq!(drug_type.normalize("Wegovy"), "Semaglutide");
        assert_eq!(drug_type.normalize("Mounjaro"), "Tirzepatide");
        assert_eq!(drug_type.normalize("Semaglutide"), "Semaglutide"); // Already canonical
    }

    #[test]
    fn test_indication_normalization() {
        let domain = definition();
        let indication_type = domain.get_entity_type("Indication").expect("Indication type");

        assert_eq!(indication_type.normalize("T2D"), "Type2Diabetes");
        assert_eq!(indication_type.normalize("T2DM"), "Type2Diabetes");
    }

    #[test]
    fn test_efficacy_schema() {
        let domain = definition();
        let schema = domain.get_schema("efficacy").expect("efficacy schema");

        assert_eq!(schema.subject_pattern, "{Drug}:{Indication}");
        assert!(schema.predicates.contains(&"hba1c_reduction_percent".to_string()));
        assert!(schema.predicates.contains(&"weight_loss_percent".to_string()));
    }

    #[test]
    fn test_safety_schema() {
        let domain = definition();
        let schema = domain.get_schema("safety").expect("safety schema");

        assert_eq!(schema.subject_pattern, "{Drug}");
        assert!(schema.predicates.contains(&"has_boxed_warning".to_string()));
        assert!(schema.predicates.contains(&"gastroparesis_risk".to_string()));
    }

    #[test]
    fn test_predicate_to_schema_lookup() {
        let domain = definition();

        // Efficacy predicate
        let schema =
            domain.schema_for_predicate("hba1c_reduction_percent").expect("should find schema");
        assert_eq!(schema.subject_pattern, "{Drug}:{Indication}");

        // Safety predicate
        let schema = domain.schema_for_predicate("has_boxed_warning").expect("should find schema");
        assert_eq!(schema.subject_pattern, "{Drug}");

        // Unknown predicate
        assert!(domain.schema_for_predicate("unknown_predicate").is_none());
    }

    #[test]
    fn test_source_hierarchy() {
        let domain = definition();
        let hierarchy = &domain.source_hierarchy;

        assert_eq!(hierarchy.len(), 6);
        assert_eq!(hierarchy[0].source_class, SourceClass::Regulatory);
        assert_eq!(hierarchy[5].source_class, SourceClass::Anecdotal);

        // Weights should decrease
        for i in 1..hierarchy.len() {
            assert!(
                hierarchy[i - 1].weight >= hierarchy[i].weight,
                "Tier {} weight should be >= tier {} weight",
                i - 1,
                i
            );
        }
    }

    #[test]
    fn test_all_predicates() {
        let domain = definition();
        let all_predicates = domain.all_predicates();

        // Should include predicates from all schemas
        assert!(all_predicates.contains(&"hba1c_reduction_percent"));
        assert!(all_predicates.contains(&"has_boxed_warning"));
        assert!(all_predicates.contains(&"primary_target"));
        assert!(all_predicates.contains(&"fda_approval_date"));
    }

    #[test]
    fn test_comparison_schema() {
        let domain = definition();
        let schema = domain.get_schema("comparison").expect("comparison schema");

        assert_eq!(schema.subject_pattern, "{Drug}:{Comparator}:{Indication}");
        assert_eq!(schema.required_entities, vec!["Drug", "Comparator", "Indication"]);
    }
}
