from io import StringIO
from unittest import TestCase

import pytest

from micall.resistance.genreport import ReportTemplate, ReportPage, read_config, read_resistance, read_mutations


class GenReportTest(TestCase):
    def test_unknown_keys(self):
        with self.assertRaisesRegex(
                ValueError,
                r"Unknown configuration: bogus_key, weird_key."):
            ReportTemplate(dict(weird_key="Hello",
                                known_regions=['R1', 'R2'],
                                bogus_key=42))

    def test_missing_keys(self):
        with self.assertRaisesRegex(
                ValueError,
                r"Missing configuration: failure_message, generated_by_text, "
                r"known_drug_classes, known_drugs, report_title, "
                r"resistance_level_colours."):
            ReportTemplate(dict(known_regions=['R1', 'R2'],
                                disclaimer_text="Hello."),
                           raise_missing=True)

    def test_register_regions(self):
        page1 = ReportTemplate(dict(known_regions=['R1', 'R2']))
        page2 = ReportTemplate(dict(known_regions=['R3']))
        expected_regions = {'R1': page1,
                            'R2': page1,
                            'R3': page2}
        regions = {}

        page1.register_regions(regions)
        page2.register_regions(regions)

        self.assertEqual(expected_regions, regions)

    def test_register_drug_classes(self):
        page1 = ReportTemplate(dict(known_drug_classes=[('C1', 'Class 1'),
                                                        ('C2', 'Class 2')],
                                    known_drugs={'C1': [('D1', 'Drug 1')],
                                                 'C2': [('D2', 'Drug 2')]}))
        page2 = ReportTemplate(dict(known_drug_classes=[('C3', 'Class 3')],
                                    known_drugs={'C3': [('D3', 'Drug 3')]}))
        expected_drug_classes = {'C1': page1,
                                 'C2': page1,
                                 'C3': page2}
        drug_classes = {}

        page1.register_drug_classes(drug_classes)
        page2.register_drug_classes(drug_classes)

        self.assertEqual(expected_drug_classes, drug_classes)

    def test_repr(self):
        page = ReportTemplate({'report_title': 'Example Report',
                               'known_regions': ['R1', 'R2']})
        expected_repr = "ReportTemplate({'report_title': 'Example Report'})"

        r = repr(page)

        self.assertEqual(expected_repr, r)

    def test_get_reported_drug_classes(self):
        template = ReportTemplate(dict(known_drug_classes=[('C1', 'Class 1'),
                                                           ('C2', 'Class 2')],
                                       known_drugs={'C1': [('D1', 'Drug 1')],
                                                    'C2': [('D2', 'Drug 2')]}))
        genotype = 'g1'
        template.genotype_pages[genotype] = ReportPage(
            resistance_calls={'D1': 'Some resistance data'},
            mutations={})
        expected_genotypes = [genotype]
        expected_drug_classes = {'C1'}
        expected_drugs = {'D1'}

        reported_genotypes = template.get_reported_genotypes()
        reported_drug_classes = template.get_reported_drug_classes(genotype)
        reported_drugs = template.get_reported_drugs(genotype)

        self.assertEqual(expected_genotypes, reported_genotypes)
        self.assertEqual(expected_drug_classes, reported_drug_classes)
        self.assertEqual(expected_drugs, reported_drugs)


def test_read_config():
    expected_prefix = 'Generated by MiCall vX.Y on Illumina BaseSpace'
    report_templates = read_config('vX.Y')

    template = report_templates[0]
    assert template.virus_config['generated_by_text'].startswith(expected_prefix)


def test_bad_config_type():
    match = (r"Configuration in .*/genreport\.yaml must be a\s+single dict "
             r"class, but found a <class 'str'>")
    with pytest.raises(ValueError, match=match):
        ReportTemplate('some bogus configuration')


def test_bad_colour_config():
    config = dict(resistance_level_colours={
        1: ['Susceptible', 0xDEFBDE]
    })

    match = (r"Error in configuration file: resistance_level_colours must have "
             r"3 entries: \['Susceptible', 14613470]")
    with pytest.raises(ValueError, match=(r"%s" % match)):
        ReportTemplate(config)


def test_bad_colour_type():
    config = dict(resistance_level_colours={
        1: ['Susceptible', 0xDEFBDE, 'blue']
    })

    match = (r"Error in configuration file: resistance_level_colours "
             r"string, int, int expected \['Susceptible', 14613470, 'blue']")
    with pytest.raises(ValueError, match=(r"%s" % match)):
        ReportTemplate(config)


def test_inconsistent_drug_classes():
    config = dict(known_drugs=dict(class_a=[('code1', 'name 1a')],
                                   class_b=[('code3', 'name 3b')]),
                  known_drug_classes=[('class_a', 'name a'),
                                      ('class_c', 'name c')])

    match = (r"Error in configuration file: known_drugs inconsistent with "
             r"drug_classes")
    with pytest.raises(ValueError, match=(r"%s" % match)):
        ReportTemplate(config)


def test_duplicate_drug_codes():
    config = dict(known_drugs=dict(class_a=[('code1', 'name 1a'),
                                            ('code2a', 'name 2a'),
                                            ('code3', 'name 3a')],
                                   class_b=[('code1', 'name 1b'),
                                            ('code2b', 'name 2b'),
                                            ('code3', 'name 3b')]),
                  known_drug_classes=[('class_a', 'name a'),
                                      ('class_b', 'name b')])

    match = (r"Error in configuration file: known_drugs duplicate drug "
             r"identifiers: code1, code3.")
    with pytest.raises(ValueError, match=(r"%s" % match)):
        ReportTemplate(config)


def test_duplicate_drug_names():
    config = dict(known_drugs=dict(class_a=[('code1a', 'name 1a'),
                                            ('code2a', 'name 2'),
                                            ('code3a', 'name 3')],
                                   class_b=[('code1b', 'name 1b'),
                                            ('code2b', 'name 2'),
                                            ('code3b', 'name 3')]),
                  known_drug_classes=[('class_a', 'name a'),
                                      ('class_b', 'name b')])

    match = (r"Error in configuration file: known_drugs duplicate drug names: "
             r"name 2, name 3.")
    with pytest.raises(ValueError, match=(r"%s" % match)):
        ReportTemplate(config)


def test_read_resistance():
    resistance_csv = StringIO("""\
region,drug_class,drug,drug_name,level,level_name,score,genotype,seed,coord_region,version
RT,NRTI,3TC,lamivudine,1,Susceptible,0.0,,HIV1B-seed,RT,9.0
""")
    config = read_config('vX.Y')
    report_template = config[0]
    regions = {}
    report_template.register_regions(regions)
    read_resistance(regions, resistance_csv)

    page = report_template.genotype_pages['']

    assert page.resistance_calls['3TC'] == (1, 'Susceptible')


def test_read_mutations():
    mutations_csv = StringIO("""\
drug_class,mutation,prevalence,genotype,region,seed,coord_region,version
NRTI,M41L,0.06,,RT,HIV1B-seed,RT,9.0
""")
    config = read_config('vX.Y')
    report_template = config[0]
    drug_classes = {}
    report_template.register_drug_classes(drug_classes)
    read_mutations(drug_classes, mutations_csv)

    page = report_template.genotype_pages['']

    assert page.mutations['NRTI'] == 'M41L(6%)'
