from pathlib import Path
from typing import List

from metacarp.camel_gene_detection.app.camel import Camel
from metacarp.camel_gene_detection.app.components.genedetection.genedetectionsrst2hit import GeneDetectionSRST2Hit
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.loggers import logger
from metacarp.camel_gene_detection.app.tools.tool import Tool


class SRST2HitExtractor(Tool):
    """
    This tool extracts hits from SRST2 output.

    INPUT:
        - TSV: SRST2 output file

    OUTPUT:
        - TSV: Tabular output file with the hits
        - VAL_Hits: List os SRST2 Hit objects
    """

    def __init__(self, camel: Camel) -> None:
        """
        Initializes this tool.
        :param camel: CAMEL instance
        """
        super().__init__('Gene Detection: SRST2 Hit Extractor', '0.1', camel)

    def _execute_tool(self) -> None:
        """
        Executes this tool.
        :return: None
        """
        hits = self.__parse_srst2_output(Path(self._tool_inputs['TSV'][0].path)) if len(
            self._tool_inputs['TSV']) > 0 else []
        self._tool_outputs['VAL_Hits'] = sorted([ToolIOValue(h) for h in hits], key=lambda v: v.value.locus)

    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")
        super()._check_input()

    def __parse_srst2_output(self, output_file: Path) -> List[GeneDetectionSRST2Hit]:
        """
        Parses the output file generated by SRST2.
        :param output_file: Output file
        :return: Parsed gene detection hits
        """
        hits = []
        with output_file.open() as handle:
            header_indices = {k: i for i, k in enumerate(handle.readline().strip().split('\t'))}
            for parts in [line.strip().split('\t') for line in handle.readlines()]:
                # Create hit object
                seq_id = parts[header_indices['allele']]
                hits.append(GeneDetectionSRST2Hit(
                    parts[2], seq_id, None,
                    int(parts[header_indices['length']]),
                    parts[header_indices['diffs']],
                    parts[header_indices['uncertainty']],
                    float(parts[header_indices['coverage']]),
                    float(parts[header_indices['divergence']]),
                    float(parts[header_indices['depth']])
                ))
        logger.info(f"{len(hits)} hits parsed")
        return hits
