use serde::{Deserialize, Serialize};

use crate::errors::LabGeneticsError;
use crate::vcf::Variant;

/// A known genetic variant in the catalog
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VariantEntry {
    /// Canonical ID (e.g., "mthfr_c677t")
    pub id: String,
    /// rsID (e.g., "rs1801133")
    #[serde(default)]
    pub rsid: String,
    /// Gene symbol
    pub gene: String,
    /// Common variant name (e.g., "C677T")
    pub variant_name: String,
    /// Chromosome
    #[serde(default)]
    pub chrom: String,
    /// Position (GRCh38)
    #[serde(default)]
    pub pos: u64,
    /// Reference allele
    #[serde(default)]
    pub ref_allele: String,
    /// Alternate allele
    #[serde(default)]
    pub alt_allele: String,
    /// Clinical category
    pub category: String,
    /// ClinVar pathogenicity classification
    pub pathogenicity: String,
    /// Is this actionable (clinical intervention exists)?
    #[serde(default)]
    pub actionable: bool,
    /// Aliases for matching
    #[serde(default)]
    pub aliases: Vec<String>,
    /// Inheritance pattern
    #[serde(default)]
    pub inheritance: String,
    /// Allele frequency in gnomAD (global)
    #[serde(default)]
    pub allele_frequency: f64,
    /// Impact description per genotype
    #[serde(default)]
    pub genotype_impacts: Vec<GenotypeImpact>,
    /// Pharmacogenomics star alleles (for PGx variants)
    #[serde(default)]
    pub pgx_alleles: Vec<PgxAllele>,
    /// Gene-gene interactions
    #[serde(default)]
    pub interactions: Vec<GeneInteraction>,
    /// Clinical notes and recommendations
    #[serde(default)]
    pub clinical_note: String,
    /// Evidence sources (PMIDs)
    #[serde(default)]
    pub sources: Vec<String>,
}

/// Impact description for a specific genotype
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GenotypeImpact {
    /// Genotype (e.g., "CT", "TT", "e3/e4")
    pub genotype: String,
    /// Risk level: benign, carrier, moderate_risk, high_risk
    pub risk: String,
    /// Description of impact
    pub description: String,
    /// Recommended actions
    #[serde(default)]
    pub actions: Vec<String>,
}

/// Pharmacogenomics allele definition
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PgxAllele {
    /// Star allele (e.g., "*2", "*3")
    pub allele: String,
    /// Function: normal, decreased, no_function, increased
    pub function: String,
    /// Affected drugs
    pub drugs: Vec<String>,
    /// Clinical recommendation
    pub recommendation: String,
}

/// Gene-gene interaction
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GeneInteraction {
    /// Interacting gene
    pub gene: String,
    /// Interaction type: additive, synergistic, antagonistic
    pub interaction_type: String,
    /// Description
    pub description: String,
}

/// The full variant catalog
#[derive(Debug, Deserialize)]
pub struct VariantCatalog {
    #[serde(rename = "variant")]
    pub variants: Vec<VariantEntry>,
}

/// Result of analyzing a single variant against the catalog
#[derive(Debug, Serialize)]
pub struct VariantResult {
    /// Input variant
    pub input: Variant,
    /// Matched catalog entry (if found)
    pub matched: Option<VariantEntry>,
    /// Match confidence
    pub confidence: String,
    /// Risk assessment
    pub risk_level: String,
    /// Recommended actions
    pub actions: Vec<String>,
    /// Pharmacogenomics result (if applicable)
    pub pgx: Option<PgxResult>,
}

/// PGx interpretation result
#[derive(Debug, Serialize)]
pub struct PgxResult {
    pub gene: String,
    pub phenotype: String,
    pub affected_drugs: Vec<String>,
    pub recommendation: String,
}

/// Load the variant catalog from embedded TOML
pub fn load_catalog() -> Result<VariantCatalog, LabGeneticsError> {
    let toml_str = include_str!("../data/variants.toml");
    toml::from_str(toml_str).map_err(|e| LabGeneticsError::CatalogError(e.to_string()))
}

/// Analyze a list of variants against the catalog
pub fn analyze_variants(
    catalog: &VariantCatalog,
    variants: &[Variant],
) -> Result<Vec<VariantResult>, LabGeneticsError> {
    let mut results = Vec::new();

    for variant in variants {
        let matched = find_match(catalog, variant);

        let (risk_level, actions, pgx) = if let Some(ref entry) = matched {
            let risk = assess_risk(entry, variant);
            let acts = get_actions(entry, variant);
            let pgx_result = if !entry.pgx_alleles.is_empty() {
                Some(interpret_pgx(entry, variant))
            } else {
                None
            };
            (risk, acts, pgx_result)
        } else {
            ("unknown".to_string(), vec![], None)
        };

        let confidence = if matched.is_some() {
            "high".to_string()
        } else {
            "unmatched".to_string()
        };

        results.push(VariantResult {
            input: variant.clone(),
            matched,
            confidence,
            risk_level,
            actions,
            pgx,
        });
    }

    Ok(results)
}

/// Find a matching catalog entry for a variant
fn find_match(catalog: &VariantCatalog, variant: &Variant) -> Option<VariantEntry> {
    let id_lower = variant.id.to_lowercase();
    let gene_lower = variant.gene.to_lowercase();
    let name_lower = variant.common_name.to_lowercase();

    for entry in &catalog.variants {
        // Match by rsID
        if !entry.rsid.is_empty() && entry.rsid.to_lowercase() == id_lower {
            return Some(entry.clone());
        }
        // Match by gene + variant name
        if !gene_lower.is_empty()
            && entry.gene.to_lowercase() == gene_lower
            && !name_lower.is_empty()
            && entry.variant_name.to_lowercase() == name_lower
        {
            return Some(entry.clone());
        }
        // Match by catalog ID
        if entry.id.to_lowercase() == id_lower {
            return Some(entry.clone());
        }
        // Match by aliases
        if entry
            .aliases
            .iter()
            .any(|a| a.to_lowercase() == id_lower || a.to_lowercase() == name_lower)
        {
            return Some(entry.clone());
        }
    }

    None
}

/// Assess risk level based on genotype
fn assess_risk(entry: &VariantEntry, variant: &Variant) -> String {
    // Find genotype-specific impact
    let gt = &variant.genotype;
    for impact in &entry.genotype_impacts {
        if impact.genotype.to_lowercase() == gt.to_lowercase()
            || variant.common_name.to_lowercase().contains(&impact.genotype.to_lowercase())
        {
            return impact.risk.clone();
        }
    }

    // Default based on zygosity and pathogenicity
    match (variant.zygosity.as_str(), entry.pathogenicity.as_str()) {
        (_, "benign") => "low".to_string(),
        ("homozygous", "pathogenic") => "high".to_string(),
        ("heterozygous", "pathogenic") => {
            if entry.inheritance == "autosomal_dominant" {
                "high".to_string()
            } else {
                "carrier".to_string()
            }
        }
        ("homozygous", "likely_pathogenic") => "moderate".to_string(),
        ("heterozygous", "likely_pathogenic") => "low".to_string(),
        (_, "risk_factor") => "moderate".to_string(),
        _ => "unknown".to_string(),
    }
}

/// Get recommended actions for a variant
fn get_actions(entry: &VariantEntry, variant: &Variant) -> Vec<String> {
    let gt = &variant.genotype;
    for impact in &entry.genotype_impacts {
        if impact.genotype.to_lowercase() == gt.to_lowercase()
            || variant.common_name.to_lowercase().contains(&impact.genotype.to_lowercase())
        {
            if !impact.actions.is_empty() {
                return impact.actions.clone();
            }
        }
    }
    vec![]
}

/// Interpret pharmacogenomics
fn interpret_pgx(entry: &VariantEntry, _variant: &Variant) -> PgxResult {
    // Simplified: return first PGx allele interpretation
    if let Some(allele) = entry.pgx_alleles.first() {
        PgxResult {
            gene: entry.gene.clone(),
            phenotype: allele.function.clone(),
            affected_drugs: allele.drugs.clone(),
            recommendation: allele.recommendation.clone(),
        }
    } else {
        PgxResult {
            gene: entry.gene.clone(),
            phenotype: "normal".to_string(),
            affected_drugs: vec![],
            recommendation: String::new(),
        }
    }
}
