from scout.server.blueprints.variant.utils import (predictions, sv_frequencies, is_affected, 
evaluation, transcript_str, frequency, end_position, add_panel_specific_gene_info, 
update_transcripts_information, clinsig_human)

def test_clinsig_human():
    ## GIVEN a variant with clinsig info
    var_obj = {
        'clnsig': [
            {
                'value': 'Likely benign',
                'accession': 335067,
                'revstat': 'criteria_provided, single submitter',
            }
        ]
    }
    ## WHEN getting the clinsig
    clinsigs = clinsig_human(var_obj)
    ## THEN assert there where no objects returned
    clinsig_obj = next(clinsigs)
    assert clinsig_obj['human'] == 'Likely benign'
    assert clinsig_obj['link'] == "https://www.ncbi.nlm.nih.gov/clinvar/variation/335067"
    


def test_clinsig_human_no_accession():
    ## GIVEN a variant with clinsig info wothout accession number
    var_obj = {
        'clnsig': [
            {
                'value': 'Likely benign',
                'revstat': 'criteria_provided, single submitter',
            }
        ]
    }
    ## WHEN getting the clinsig
    clinsigs = clinsig_human(var_obj)
    ## THEN assert there where no objects returned since we skip those wothout accession
    i = 0
    for i, obj in enumerate(clinsigs,1):
        pass
    assert i == 0


def test_clinsig_human_no_clnsig():
    ## GIVEN a variant with clinsig info
    var_obj = {}
    ## WHEN getting the clinsig
    clinsigs = clinsig_human(var_obj)
    ## THEN assert there where no objects returned
    i = 0
    for i, obj in enumerate(clinsigs,1):
        pass
    assert i == 0

def test_update_transcripts_information_disease_associated():
    ## GIVEN a variants gene with info about disease associated tx
    variant_gene = {
        "hgnc_id": 10729, 
        "hgnc_symbol": "SEMA4A",
        "disease_associated_no_version": ["NM_001193301"],
        "transcripts": [
            {
                "transcript_id": "ENST00000355014", 
                "hgnc_id": 10729, 
                "protein_id": "ENSP00000347117", 
                "sift_prediction": "unknown", 
                "polyphen_prediction": "unknown", 
                "swiss_prot": "Q9H3S1",
                "biotype": "protein_coding", 
                "functional_annotations": ["intron_variant"], 
                "region_annotations": ["intronic"], 
                "intron": "10/14", 
                "strand": "+", 
                "coding_sequence_name": "c.1135-4554G>A", 
                "is_canonical": False
            }, 
        ], 
    }
    assert variant_gene['transcripts'][0].get('is_disease_associated') is None
    
    hgnc_gene = {
        "_id": {"$oid": "5c8112e701f54818d3cbc04a"}, 
        "hgnc_id": 10729, 
        "hgnc_symbol": "SEMA4A", 
        "ensembl_id": "ENSG00000196189", 
        "chromosome": "1", 
        "start": 156117157, 
        "end": 156147543, 
        "length": 30386, 
        "description": "semaphorin 4A", 
        "aliases": ["SEMA4A", "SEMAB", "CORD10", "SemB", "FLJ12287"], 
        "primary_transcripts": ["NM_022367"], 
        "inheritance_models": ["AD", "AR"], 
        "entrez_id": 64218, 
        "omim_id": 607292, 
        "ucsc_id": "uc001fnl.4", 
        "uniprot_ids": ["Q9H3S1"], 
        "vega_id": "OTTHUMG00000014042", 
        "pli_score": 0.615371620982326, 
        "incomplete_penetrance": False, 
        "build": "37", 
        "transcripts": [
            {
                "_id": {"$oid": "5c81130601f54818d3cc0514"}, 
                "ensembl_transcript_id": "ENST00000355014", 
                "hgnc_id": 10729, 
                "chrom": "1", 
                "start": 156119810, 
                "end": 156147535, 
                "is_primary": True, 
                "refseq_id": "NM_001193301", 
                "refseq_identifiers": ["NM_001193301"], 
                "build": "37", 
                "length": 27725
            }, 
        ]
    }

    variant_obj = {}
    ## WHEN updating the transcripts information
    update_transcripts_information(variant_gene, hgnc_gene, variant_obj, genome_build=None)
    ## THEN assert that the tx on variant gene has is disease associated
    assert variant_gene['transcripts'][0].get('is_disease_associated') is True


def test_update_transcripts_information_refseq_id():
    ## GIVEN a variants gene with no primary transcript
    variant_gene = {
        "hgnc_id": 10729, 
        "hgnc_symbol": "SEMA4A", 
        "transcripts": [
            {
                "transcript_id": "ENST00000355014", 
                "hgnc_id": 10729, 
                "protein_id": "ENSP00000347117", 
                "sift_prediction": "unknown", 
                "polyphen_prediction": "unknown", 
                "swiss_prot": "Q9H3S1",
                "biotype": "protein_coding", 
                "functional_annotations": ["intron_variant"], 
                "region_annotations": ["intronic"], 
                "intron": "10/14", 
                "strand": "+", 
                "coding_sequence_name": "c.1135-4554G>A", 
                "is_canonical": False
            }, 
        ], 
    }
    assert variant_gene['transcripts'][0].get('refseq_id') is None
    
    hgnc_gene = {
        "_id": {"$oid": "5c8112e701f54818d3cbc04a"}, 
        "hgnc_id": 10729, 
        "hgnc_symbol": "SEMA4A", 
        "ensembl_id": "ENSG00000196189", 
        "chromosome": "1", 
        "start": 156117157, 
        "end": 156147543, 
        "length": 30386, 
        "description": "semaphorin 4A", 
        "aliases": ["SEMA4A", "SEMAB", "CORD10", "SemB", "FLJ12287"], 
        "primary_transcripts": ["NM_022367"], 
        "inheritance_models": ["AD", "AR"], 
        "entrez_id": 64218, 
        "omim_id": 607292, 
        "ucsc_id": "uc001fnl.4", 
        "uniprot_ids": ["Q9H3S1"], 
        "vega_id": "OTTHUMG00000014042", 
        "pli_score": 0.615371620982326, 
        "incomplete_penetrance": False, 
        "build": "37", 
        "transcripts": [
            {
                "_id": {"$oid": "5c81130601f54818d3cc0514"}, 
                "ensembl_transcript_id": "ENST00000355014", 
                "hgnc_id": 10729, 
                "chrom": "1", 
                "start": 156119810, 
                "end": 156147535, 
                "is_primary": True, 
                "refseq_id": "NM_001193301", 
                "refseq_identifiers": ["NM_001193301"], 
                "build": "37", 
                "length": 27725
            }, 
        ]
    }

    variant_obj = {}
    ## WHEN updating the transcripts information
    update_transcripts_information(variant_gene, hgnc_gene, variant_obj, genome_build=None)
    ## THEN assert that the tx on variant gene has been given a ref seq id
    assert variant_gene['transcripts'][0].get('refseq_id') == "NM_001193301"
    ## THEN assert that the variant obj has refseq ids
    assert variant_obj['has_refseq'] is True
    ## THEN assert that the list of primary transcripts has been added
    assert len(variant_gene['primary_transcripts']) == 1

def test_update_transcripts_information_refseq_id():
    ## GIVEN a variants gene with no primary transcript
    variant_gene = {
        "hgnc_id": 10729, 
        "hgnc_symbol": "SEMA4A", 
        "transcripts": [
            {
                "transcript_id": "ENST00000355014", 
                "hgnc_id": 10729, 
                "protein_id": "ENSP00000347117", 
                "sift_prediction": "unknown", 
                "polyphen_prediction": "unknown", 
                "swiss_prot": "Q9H3S1",
                "biotype": "protein_coding", 
                "functional_annotations": ["intron_variant"], 
                "region_annotations": ["intronic"], 
                "intron": "10/14", 
                "strand": "+", 
                "coding_sequence_name": "c.1135-4554G>A", 
                "is_canonical": False
            }, 
        ], 
    }
    assert variant_gene['transcripts'][0].get('refseq_id') is None
    
    hgnc_gene = {
        "_id": {"$oid": "5c8112e701f54818d3cbc04a"}, 
        "hgnc_id": 10729, 
        "hgnc_symbol": "SEMA4A", 
        "ensembl_id": "ENSG00000196189", 
        "chromosome": "1", 
        "start": 156117157, 
        "end": 156147543, 
        "length": 30386, 
        "description": "semaphorin 4A", 
        "aliases": ["SEMA4A", "SEMAB", "CORD10", "SemB", "FLJ12287"], 
        "primary_transcripts": ["NM_022367"], 
        "inheritance_models": ["AD", "AR"], 
        "entrez_id": 64218, 
        "omim_id": 607292, 
        "ucsc_id": "uc001fnl.4", 
        "uniprot_ids": ["Q9H3S1"], 
        "vega_id": "OTTHUMG00000014042", 
        "pli_score": 0.615371620982326, 
        "incomplete_penetrance": False, 
        "build": "37", 
        "transcripts": [
            {
                "_id": {"$oid": "5c81130601f54818d3cc0514"}, 
                "ensembl_transcript_id": "ENST00000355014", 
                "hgnc_id": 10729, 
                "chrom": "1", 
                "start": 156119810, 
                "end": 156147535, 
                "is_primary": True, 
                "refseq_id": "NM_001193301", 
                "refseq_identifiers": ["NM_001193301"], 
                "build": "37", 
                "length": 27725
            }, 
        ]
    }

    variant_obj = {}
    ## WHEN updating the transcripts information
    update_transcripts_information(variant_gene, hgnc_gene, variant_obj, genome_build=None)
    ## THEN assert that the tx on variant gene has been given a ref seq id
    assert variant_gene['transcripts'][0].get('refseq_id') == "NM_001193301"
    ## THEN assert that the variant obj has refseq ids
    assert variant_obj['has_refseq'] is True
    ## THEN assert that the list of primary transcripts has been added
    assert len(variant_gene['primary_transcripts']) == 1
    

def test_update_transcripts_information_is_primary():
    ## GIVEN a variants gene with no primary transcript
    variant_gene = {
        "hgnc_id": 10729, 
        "hgnc_symbol": "SEMA4A", 
        "transcripts": [
            {
                "transcript_id": "ENST00000355014", 
                "hgnc_id": 10729, 
                "protein_id": "ENSP00000347117", 
                "sift_prediction": "unknown", 
                "polyphen_prediction": "unknown", 
                "swiss_prot": "Q9H3S1",
                "biotype": "protein_coding", 
                "functional_annotations": ["intron_variant"], 
                "region_annotations": ["intronic"], 
                "intron": "10/14", 
                "strand": "+", 
                "coding_sequence_name": "c.1135-4554G>A", 
                "is_canonical": False
            }, 
        ], 
    }
    assert variant_gene['transcripts'][0].get('is_primary') is None
    
    hgnc_gene = {
        "_id": {"$oid": "5c8112e701f54818d3cbc04a"}, 
        "hgnc_id": 10729, 
        "hgnc_symbol": "SEMA4A", 
        "ensembl_id": "ENSG00000196189", 
        "chromosome": "1", 
        "start": 156117157, 
        "end": 156147543, 
        "length": 30386, 
        "description": "semaphorin 4A", 
        "aliases": ["SEMA4A", "SEMAB", "CORD10", "SemB", "FLJ12287"], 
        "primary_transcripts": ["NM_022367"], 
        "inheritance_models": ["AD", "AR"], 
        "entrez_id": 64218, 
        "omim_id": 607292, 
        "ucsc_id": "uc001fnl.4", 
        "uniprot_ids": ["Q9H3S1"], 
        "vega_id": "OTTHUMG00000014042", 
        "pli_score": 0.615371620982326, 
        "incomplete_penetrance": False, 
        "build": "37", 
        "transcripts": [
            {
                "_id": {"$oid": "5c81130601f54818d3cc0514"}, 
                "ensembl_transcript_id": "ENST00000355014", 
                "hgnc_id": 10729, 
                "chrom": "1", 
                "start": 156119810, 
                "end": 156147535, 
                "is_primary": True, 
                "refseq_id": "NM_001193301", 
                "refseq_identifiers": ["NM_001193301"], 
                "build": "37", 
                "length": 27725
            }, 
        ]
    }

    variant_obj = {}
    ## WHEN updating the transcripts information
    update_transcripts_information(variant_gene, hgnc_gene, variant_obj, genome_build=None)
    ## THEN assert that the tx on variant gene has been labeled primary transcript
    assert variant_gene['transcripts'][0].get('is_primary') is True

def test_update_transcripts_information(hgnc_gene, variant_gene):
    ## GIVEN a hgnc gene, a variant gene and a variant object
    variant_obj = {}
    assert 'primary_transcripts' not in variant_gene
    ## WHEN updating the transcript information for the variant gene
    update_transcripts_information(variant_gene, hgnc_gene, variant_obj)
    ## THEN assert the function has completed without errors
    assert 'primary_transcripts' in variant_gene

def test_add_panel_specific_gene_info_disease_tx():
    ## GIVEN some panel genes and a gene object
    panel_info = [
        {
            'hgnc_id':100, 
            'symbol': 'AAA', 
            'disease_associated_transcripts': ['NM001.1']
        }
    ]
    ## WHEN parsing the info
    panel_info = add_panel_specific_gene_info(panel_info)
    ## THEN assert no info is returned
    assert panel_info.get('mosaicism') is False
    assert panel_info.get('disease_associated_no_version') == set(['NM001'])
    assert panel_info.get('disease_associated_transcripts') == ['NM001.1']


def test_add_panel_specific_gene_info_mosaic():
    ## GIVEN some panel genes and a gene object
    panel_info = [{'hgnc_id':100, 'symbol': 'AAA', 'mosaicism': True}]
    ## WHEN parsing the info
    panel_info = add_panel_specific_gene_info(panel_info)
    ## THEN assert no info is returned
    assert panel_info['mosaicism'] is True

def test_add_panel_specific_gene_info_empty():
    ## GIVEN some panel genes and a gene object
    panel_info = []
    ## WHEN fetching the panel specific info
    panel_info = add_panel_specific_gene_info(panel_info)
    ## THEN assert no info is returned
    for entry in panel_info:
        assert not panel_info[entry]

def test_add_panel_specific_gene_info():
    ## GIVEN some panel genes and a gene object
    panel_info = [{'hgnc_id':100, 'symbol': 'AAA'}]
    ## WHEN parsing the info
    panel_info = add_panel_specific_gene_info(panel_info)
    ## THEN assert no info is returned
    for entry in panel_info:
        assert not panel_info[entry]

def test_end_position_old_indel():
    ## GIVEN a small indel
    var = {'alternative':'TCTC', 'reference': 'AGAG', 'position': 100}
    ## WHEN getting the end position
    end = end_position(var)
    ## THEN assert that the end position is 10 bases
    assert end == 103

def test_end_position_indel():
    ## GIVEN a single nucleotide variant
    var = {'alternative':'TCTCTCTCACA', 'reference': 't', 'position': 100}
    ## WHEN getting the end position
    end = end_position(var)
    ## THEN assert that the end position is 10 bases
    assert end == 110

def test_end_position_snv():
    ## GIVEN a single nucleotide variant
    var = {'alternative':'A', 'reference': 'C', 'position': 100}
    ## WHEN getting the end position
    end = end_position(var)
    ## THEN assert that the end position is correct
    assert end == 100

def test_frequency_rare():
    ## GIVEN a variant which is uncommon gnomad frequency
    var = {'gnomad_frequency':0.001}
    ## WHEN converting frequencies to string
    freq_str = frequency(var)
    ## THEN assert that the variant is common
    assert freq_str == 'rare'

def test_frequency_uncommon():
    ## GIVEN a variant which is uncommon gnomad frequency
    var = {'gnomad_frequency':0.03}
    ## WHEN converting frequencies to string
    freq_str = frequency(var)
    ## THEN assert that the variant is common
    assert freq_str == 'uncommon'

def test_frequency_common():
    ## GIVEN a variant with a common gnomad frequency
    var = {'gnomad_frequency':0.5}
    ## WHEN converting frequencies to string
    freq_str = frequency(var)
    ## THEN assert that the variant is common
    assert freq_str == 'common'
    
def test_tx_str():
    ## GIVEN a transcript object with an exon change and a gene name
    gene_name = 'NCDN'
    tx_obj = {
        'exon': '8/8',
        'refseq_id': 'NM_001014839',
        'coding_sequence_name': 'c.*156C>T',
    }
    ## WHEN Converting the information to a string for the template
    change_str = transcript_str(tx_obj, gene_name)
    
    ## THEN assert the string is on expected format
    assert change_str == "NCDN:NM_001014839:exon8:c.*156C>T:NA"
    
def test_evaluation(real_variant_database):
    ## GIVEN a populated database
    store = real_variant_database
    var = store.variant_collection.find_one()
    user = store.user_collection.find_one()
    institute = store.institute_collection.find_one()
    case = store.case_collection.find_one()
    link = 'a link'
    classification = 'pathogenic'
    
    ## WHEN adding an evaluation to the database
    store.submit_evaluation(var, user, institute, case, link, classification=classification)
    
    eval_obj = store.acmg_collection.find_one()
    assert eval_obj['institute_id'] == institute['_id']
    assert 'institute' not in eval_obj
    
    updated_eval = evaluation(store, eval_obj)
    ## THEN assert that the correct information was added to display evaluation
    assert updated_eval['institute'] == institute
    

def test_is_affected_healthy():
    ## GIVEN a variant and a case
    case_obj = {
        'individuals': [{'individual_id': '1', 'phenotype': 1}]
    }
    variant_obj = {
        'samples': [{'sample_id': '1'}]
    }
    ## WHEN converting affection status to string
    is_affected(variant_obj, case_obj)
    ## THEN assert that affection status is healthy
    updated_sample = variant_obj['samples'][0]
    assert 'is_affected' in updated_sample
    assert updated_sample['is_affected'] == False

def test_is_affected_affected():
    ## GIVEN a variant and a case
    case_obj = {
        'individuals': [{'individual_id': '1', 'phenotype': 2}]
    }
    variant_obj = {
        'samples': [{'sample_id': '1'}]
    }
    ## WHEN converting affection status to string
    is_affected(variant_obj, case_obj)
    ## THEN assert that affection status is healthy
    updated_sample = variant_obj['samples'][0]
    assert updated_sample['is_affected'] == True

def test_gene_predictions_no_info():
    ## GIVEN a empty list of genes
    genes = []
    
    ## WHEN parsing the gene predictions
    res = predictions(genes)
    ## THEN assert the result is not filled
    assert res == {'sift_predictions': [],
                   'polyphen_predictions': [],
                   'region_annotations': [],
                   'functional_annotations': []}

def test_gene_predictions_one_gene():
    ## GIVEN a empty list of genes
    gene = {
        'sift_prediction': 'deleterious',
        'polyphen_prediction': 'probably_damaging',
        'region_annotation': 'exonic',
        'functional_annotation': 'missense_variant',
    }
    genes = [gene]
    
    ## WHEN parsing the gene predictions
    res = predictions(genes)
    ## THEN assert the result is not filled
    assert res == {
                   'sift_predictions': ['deleterious'],
                   'polyphen_predictions': ['probably_damaging'],
                   'region_annotations': ['exonic'],
                   'functional_annotations': ['missense_variant']
               }

def test_gene_predictions_one_gene_no_sift():
    ## GIVEN a empty list of genes
    gene = {
        'hgnc_symbol': 'AAA',
        'polyphen_prediction': 'probably_damaging',
        'region_annotation': 'exonic',
        'functional_annotation': 'missense_variant',
    }
    genes = [gene]
    
    ## WHEN parsing the gene predictions
    res = predictions(genes)
    ## THEN assert the result is not filled
    assert res == {
                   'sift_predictions': ['-'],
                   'polyphen_predictions': ['probably_damaging'],
                   'region_annotations': ['exonic'],
                   'functional_annotations': ['missense_variant']
               }

def test_gene_predictions_two_genes():
    ## GIVEN a empty list of genes
    gene = {
        'hgnc_symbol': 'AAA',
        'sift_prediction': 'deleterious',
        'polyphen_prediction': 'probably_damaging',
        'region_annotation': 'exonic',
        'functional_annotation': 'missense_variant',
    }
    gene2 = {
        'hgnc_symbol': 'BBB',
        'sift_prediction': 'tolerated',
        'polyphen_prediction': 'unknown',
        'region_annotation': 'exonic',
        'functional_annotation': 'synonymous_variant',
    }
    genes = [gene, gene2]
    
    ## WHEN parsing the gene predictions
    res = predictions(genes)
    ## THEN assert the result is not filled
    assert set(res['sift_predictions']) == set(['AAA:deleterious', 'BBB:tolerated'])

def test_sv_frequencies_empty():
    ## GIVEN a variant object with gnomad annotation
    var = {}
    ## WHEN parsing the sv frequencies
    freq = sv_frequencies(var)
    ## THEN assert the correct tuple is returned
    assert len(freq) == 1
    assert freq[0] == ('GnomAD', var.get('gnomad_frequency', 'NA'))

def test_sv_frequencies_gnomad():
    ## GIVEN a variant object with gnomad annotation
    var = {'gnomad_frequency': 0.01}
    ## WHEN parsing the sv frequencies
    freq = sv_frequencies(var)
    ## THEN assert the correct tuple is returned
    assert len(freq) == 1
    assert freq[0] == ('GnomAD', var.get('gnomad_frequency'))

def test_sv_frequencies_gnomad_exac():
    ## GIVEN a variant object with gnomad annotation
    var = {'exac_frequency': 0.01}
    ## WHEN parsing the sv frequencies
    freq = sv_frequencies(var)
    ## THEN assert the correct tuple is returned
    assert len(freq) == 1
    assert freq[0] == ('GnomAD', var.get('exac_frequency'))


def test_sv_frequencies_all():
    ## GIVEN a variant object with gnomad annotation
    var = {
            'gnomad_frequency': 0.02,
            'clingen_cgh_benign': 0.02,
            'clingen_cgh_pathogenic': 0.02,
            'clingen_ngi': 0.02,
            'clingen_mip': 0.02,
            'swegen': 0.02,
            'decipher': 0.02,
            'thousand_genomes_frequency': 0.02,
        }
    ## WHEN parsing the sv frequencies
    freq = sv_frequencies(var)
    ## THEN assert the correct tuple is returned
    assert len(freq) == 8
    for annotation in freq:
        assert annotation[1] == 0.02