from unittest.mock import call

import pytest

from utils.exceptions import CustomMapError
from utils.progs.acc2annot_mapper import run_acc2annot_mapper
from io import StringIO


class TestRunAcc2AnnotMapper:
    def run(self, mocker, accs, map_string, expected_result_string=None):
        m = mocker.mock_open(read_data=map_string)
        mocker.patch('builtins.open', m)
        run_acc2annot_mapper(accs, "non_existant_map", "non_existant_result")
        if expected_result_string:
            calls = [call(l) for l in StringIO(expected_result_string).readlines()]
            m().write.assert_has_calls(calls)

    def test_run_acc2annot_mapper_tiny(self, mocker):
        self.run(mocker,
                 accs=['KZK33282.1', 'NP_268346.1'],
                 map_string="""#acc	level1	level2	level3
KZK33282.1	Bacteria	Firmicutes	Bacilli
NP_268346.1	Bacteria	Firmicutes	Bacilli""",
                 expected_result_string="""#acc	source	hit	level1	level2	level3
KZK33282.1	custom_map	KZK33282.1	Bacteria	Firmicutes	Bacilli
NP_268346.1	custom_map	NP_268346.1	Bacteria	Firmicutes	Bacilli""")

    def test_run_acc2annot_mapper(self, mocker):
        self.run(mocker,
                 accs=['KZK33282.1', 'NP_268346.1', 'WP_003131952.1', 'PKC80086.1', 'XP_963622.1', 'WP_000665196.1',
                       'WP_000665196.1'],
                 map_string="""#acc	level1	level2	level3
KZK33282.1	Bacteria	Firmicutes	Bacilli
NP_268346.1	Bacteria	Firmicutes	Bacilli
WP_003131952.1	Bacteria	Firmicutes	Bacilli
PKC80086.1	Bacteria	Actinobacteria	Bifidobacteriales
XP_963622.1	Eukaryota	Ascomycota	Sordariomycetes
WP_000665196.1	Bacteria	Proteobacteria	Gammaproteobacteria
WP_000665198.1	Bacteria	Proteobacteria	Gammaproteobacteria
WP_000665196.1	Bacteria	Proteobacteria	Gammaproteobacteria group""",
                 expected_result_string="""#acc	source	hit	level1	level2	level3
KZK33282.1	custom_map	KZK33282.1	Bacteria	Firmicutes	Bacilli
NP_268346.1	custom_map	NP_268346.1	Bacteria	Firmicutes	Bacilli
WP_003131952.1	custom_map	WP_003131952.1	Bacteria	Firmicutes	Bacilli
PKC80086.1	custom_map	PKC80086.1	Bacteria	Actinobacteria	Bifidobacteriales
XP_963622.1	custom_map	XP_963622.1	Eukaryota	Ascomycota	Sordariomycetes
WP_000665196.1	custom_map	WP_000665196.1	Bacteria	Proteobacteria	Gammaproteobacteria
WP_000665196.1	custom_map	WP_000665196.1	Bacteria	Proteobacteria	Gammaproteobacteria group""")

    def test_accs_missing_in_map(self, mocker):
        self.run(mocker,
                 accs=['KZK33282.13', 'KZK33282.15', 'KZK33282.1', 'NP_268346.1', 'WP_003131952.1', 'PKC80086.1',
                       'XP_963622.1', 'WP_000665196.1', 'WP_000665196.1'],
                 map_string="""#acc	level1	level2	level3
KZK33282.1	Bacteria	Firmicutes	Bacilli
NP_268346.1	Bacteria	Firmicutes	Bacilli
WP_003131952.1	Bacteria	Firmicutes	Bacilli
PKC80086.1	Bacteria	Actinobacteria	Bifidobacteriales
XP_963622.1	Eukaryota	Ascomycota	Sordariomycetes
WP_000665196.1	Bacteria	Proteobacteria	Gammaproteobacteria
WP_000665196.1	Bacteria	Proteobacteria	Gammaproteobacteria group""",
                 expected_result_string="""#acc	source	hit	level1	level2	level3
KZK33282.1	custom_map	KZK33282.1	Bacteria	Firmicutes	Bacilli
NP_268346.1	custom_map	NP_268346.1	Bacteria	Firmicutes	Bacilli
WP_003131952.1	custom_map	WP_003131952.1	Bacteria	Firmicutes	Bacilli
PKC80086.1	custom_map	PKC80086.1	Bacteria	Actinobacteria	Bifidobacteriales
XP_963622.1	custom_map	XP_963622.1	Eukaryota	Ascomycota	Sordariomycetes
WP_000665196.1	custom_map	WP_000665196.1	Bacteria	Proteobacteria	Gammaproteobacteria
WP_000665196.1	custom_map	WP_000665196.1	Bacteria	Proteobacteria	Gammaproteobacteria group"""
                 )

    def test_error_on_empty_map(self, mocker):
        with pytest.raises(CustomMapError):
            self.run(mocker,
                     accs=['KZK33282.1', 'NP_268346.1', 'WP_003131952.1', 'PKC80086.1', 'XP_963622.1',
                           'WP_000665196.1', 'WP_000665196.1'],
                     map_string="""""")

    def test_error_on_malformed_map(self, mocker):
        with pytest.raises(CustomMapError):
            self.run(mocker,
                     accs=['KZK33282.1', 'NP_268346.1', 'WP_003131952.1'],
                     map_string="""#acc	level1	level2	level3
KZK33282.1	Bacteria	Firmicutes	Bacilli
NP_268346.1	Bacteria	Firmicutes	Bacilli
WP_003131952.1	Bacteria	Firmicutes""")
