from typing import Dict, Any, List

from metacarp.camel_gene_detection.app.camel import Camel
from metacarp.camel_gene_detection.app.components.sequencetyping.sequencetypingkmahit import SequenceTypingKMAHit
from metacarp.camel_gene_detection.app.components.sequencetyping.sequencetypingutils import SequenceTypingUtils
from metacarp.camel_gene_detection.app.error.invalidinputspecificationerror import InvalidInputSpecificationError
from metacarp.camel_gene_detection.app.io.tooliovalue import ToolIOValue
from metacarp.camel_gene_detection.app.tools.tool import Tool


class KMATypingHitExtractor(Tool):
    """
    This tool extracts typing hits from the output generated by KMA.
    """

    def __init__(self, camel: Camel) -> None:
        """
        Initializes this tool.
        :param camel: CAMEL instance
        """
        super().__init__('KMA typing hit extractor', '1.0', camel)

    def _check_input(self) -> None:
        """
        Checks if the provided input is valid.
        :return: None
        """
        if 'TSV' not in self._tool_inputs:
            raise InvalidInputSpecificationError('TSV input is required')
        if 'locus' not in self._input_informs:
            raise InvalidInputSpecificationError('Locus information is required')
        super()._check_input()

    def _execute_tool(self) -> None:
        """
        Executes this tool.
        :return: None
        """
        metadata_locus = self._input_informs['locus']
        hits = KMATypingHitExtractor.__parse_kma_output(self._tool_inputs['TSV'][0].path, metadata_locus)
        hits_filtered_pident = [h for h in hits if h.percent_identity > 90]
        hits_filtered_cov = [h for h in hits_filtered_pident if h.percent_covered > 60]
        if len(hits_filtered_cov) > 0:
            best_hit = sorted(hits_filtered_cov, key=lambda x: -x.score)[0]
        else:
            best_hit = SequenceTypingKMAHit.create_empty_hit(metadata_locus['name'])
        self._tool_outputs['VAL_hit'] = [ToolIOValue(best_hit)]

    @staticmethod
    def __parse_kma_output(path_kma_out: str, metadata_locus: Dict[str, Any]) -> List[SequenceTypingKMAHit]:
        """
        Parses the hits from the KMA output file.
        :param path_kma_out: KMA output file path
        :return: List of parsed hits
        """
        with open(path_kma_out) as handle:
            lines = handle.readlines()
            if len(lines) < 2:
                return []
            header_indices = {h: i for i, h in enumerate(lines[0].strip().split('\t'))}
            hits = []
            for line in lines[1:]:
                parts = [x.strip() for x in line.split('\t')]
                allele_name = parts[header_indices['#Template']]
                hits.append(SequenceTypingKMAHit(
                    metadata_locus['name'],
                    SequenceTypingUtils.get_allele_id(allele_name, metadata_locus['allele_id_regex']),
                    int(parts[header_indices['Template_length']]),
                    float(parts[header_indices['Template_Identity']]),
                    float(parts[header_indices['Template_Coverage']]),
                    float(parts[header_indices['Depth']]),
                    int(parts[header_indices['Score']])
                ))
        return hits
