from typing import List, Dict, Literal, get_args

ACMGCode = Literal[
    "PVS1", "PS1", "PS2", "PS3", "PS4",
    "PM1", "PM2", "PM3", "PM4", "PM5", "PM6",
    "PP1", "PP2", "PP3", "PP4", "PP5",
    "BA1", "BS1", "BS2", "BS3", "BS4",
    "BP1", "BP2", "BP3", "BP4", "BP5", "BP6", "BP7"
]

PathogenicityResult = Literal[
    "Pathogenic",
    "Likely Pathogenic",
    "Uncertain Significance",
    "Likely Benign",
    "Benign"
]

VALID_CODES = set(get_args(ACMGCode))

def metadata() -> Dict[str, str]:
    return {
        "PVS1": "Null variant in a gene where LOF is a known mechanism of disease",
        "PS1": "Same amino acid change as a previously established pathogenic variant",
        "PS2": "De novo (both maternity and paternity confirmed)",
        "PS3": "Well-established in vitro or in vivo functional studies supportive of a pathogenic effect",
        "PS4": "The prevalence of the variant in affected individuals is significantly increased compared with the prevalence in controls",
        "PM1": "Located in a mutational hot spot and/or functional domain",
        "PM2": "Absent from controls",
        "PM3": "For recessive disorders, detected in trans with a pathogenic variant",
        "PM4": "Protein length changes as a result of in-frame deletions/insertions",
        "PM5": "Novel missense change at an amino acid residue where a different missense change determined to be pathogenic has been seen before",
        "PM6": "Assumed de novo, but without confirmation of paternity and maternity",
        "PP1": "Cosegregation with disease in multiple affected family members",
        "PP2": "Missense variant in a gene that has a low rate of benign missense variation",
        "PP3": "Multiple lines of computational evidence support a deleterious effect on the gene",
        "PP4": "Patient's phenotype or family history is highly specific for a disease with a single genetic cause",
        "PP5": "Reputable source recently reports variant as pathogenic",
        "BA1": "Allele frequency is >5% in control population",
        "BS1": "Allele frequency is greater than expected for disorder",
        "BS2": "Observed in a healthy adult individual",
        "BS3": "Well-established in vitro or in vivo functional studies show no damaging effect on protein function",
        "BS4": "Lack of segregation in affected members of a family",
        "BP1": "Missense variant in a gene for which primarily truncating variants are known to cause disease",
        "BP2": "Observed in trans with a pathogenic variant for a fully penetrant dominant gene/disorder",
        "BP3": "In-frame deletions/insertions in a repetitive region without a known function",
        "BP4": "Multiple lines of computational evidence suggest no impact on gene",
        "BP5": "Variant found in a case with an alternate molecular basis for disease",
        "BP6": "Reputable source recently reports variant as benign",
        "BP7": "A synonymous variant for which splicing prediction algorithms predict no effect"
    }

def calculate_pathogenicity(evidence: List[ACMGCode]) -> PathogenicityResult:
    for code in evidence:
        if code not in VALID_CODES:
            raise ValueError(f"Unknown or invalid evidence code: {code}")

    v_strong = len([c for c in evidence if c.startswith('PVS')])
    strong = len([c for c in evidence if c.startswith('PS')])
    moderate = len([c for c in evidence if c.startswith('PM')])
    supporting = len([c for c in evidence if c.startswith('PP')])
    b_standalone = len([c for c in evidence if c == 'BA1'])
    b_strong = len([c for c in evidence if c.startswith('BS')])
    b_supporting = len([c for c in evidence if c.startswith('BP')])

    # Benign Rules
    if b_standalone >= 1 or b_strong >= 2:
        return "Benign"
    
    # Likely Benign Rules
    if (b_strong == 1 and b_supporting >= 1) or (b_supporting >= 2):
        return "Likely Benign"

    # Pathogenic Rules
    if (v_strong >= 1 and (strong >= 1 or moderate >= 2 or (moderate == 1 and supporting >= 1) or supporting >= 2)) or \
       (strong >= 2) or \
       (strong == 1 and (moderate >= 3 or (moderate == 2 and supporting >= 2) or (moderate == 1 and supporting >= 4))):
        return "Pathogenic"

    # Likely Pathogenic Rules
    if (v_strong == 1 and moderate == 1) or \
       (strong == 1 and (moderate >= 1 and moderate <= 2)) or \
       (strong == 1 and supporting >= 2) or \
       (moderate >= 3) or \
       (moderate == 2 and supporting >= 2) or \
       (moderate == 1 and supporting >= 4):
        return "Likely Pathogenic"

    # Uncertain Significance if no other rules met or if conflicting
    return "Uncertain Significance"
