from __future__ import annotations

import shutil
from os.path import basename, dirname, splitext
from os.path import join as pjoin

from ugbio_core.vcf_utils import VcfUtils
from ugbio_core.vcfbed.interval_file import IntervalFile

from ugbio_comparison.vcf_comparison_utils import VcfComparisonUtils


class ComparisonPipeline:  # pylint: disable=too-many-instance-attributes
    """
    Run comparison between the two sets of calls: input_prefix and truth_file. Creates
    a combined call file and a concordance VCF by either of the concordance tools
    """

    # pylint: disable=too-many-arguments
    def __init__(  # noqa: PLR0913
        self,
        vcu: VcfComparisonUtils,
        vu: VcfUtils,
        input_prefix: str,
        truth_file: str,
        cmp_intervals: IntervalFile,
        highconf_intervals: IntervalFile,
        ref_genome: str,
        call_sample: str,
        truth_sample: str,
        output_file_name: str,
        sdf_index: str | None = None,
        output_suffix: str | None = None,
        *,
        ignore_filter: bool = False,
        revert_hom_ref: bool = False,
    ):
        """
        Parameters
        ----------
        vcu: VcfComparisonUtils
            VcfComparisonUtils object for executing common functions via unix shell
        vu: VcfUtils
            VcfUtils object for VCF utility functions
        input_prefix : str
            Input prefix for the vcf. The script will look for <input_prefix>.vcf.gz
        truth_file : str, optional
            Truth calls file
        cmp_intervals : vcf_pipeline_utils.IntervalFile, optional
            interval_list file over which to do comparison (e.g. chr9)
        highconf_intervals : IntervalFile
            high confidence intervals for the ground truth
        ref_genome : str
            Reference genome FASTA
        sdf_index : str, optional
            VCFEVAL SDF index for the reference genome. If None, will use ref_genome + ".sdf"
        call_sample : str, optional
            Name of the calls sample
        truth_sample : str, optional
            Name of the truth sample
        output_file_name: str
            Name of the output file - will determine the name of the vcfeval output directory.
            The output_file_name should include the output_dir and output_dir and output_file_name
            are mutually exclusive
        output_suffix : str, optional
            Suffix for the output file name (e.g. chr9) -
            otherwise the output file nams are starting with the input prefix
        ignore_filter : bool, optional
            Should the filter status **of calls only** be ignored. Filter status of truth is always
            taken into account
        revert_hom_ref : bool, optional
            Should the hom ref calls filtered out be reverted (deepVariant only)
        """
        self.vcu = vcu
        self.vu = vu
        self.input_prefix = input_prefix
        self.truth_file = truth_file
        self.cmp_intervals = cmp_intervals
        self.ref_genome = ref_genome
        self.sdf_index = sdf_index
        self.highconf_intervals = highconf_intervals
        self.call_sample = call_sample
        self.truth_sample = truth_sample
        self.output_file_name = output_file_name
        self.ignore_filter = ignore_filter
        self.revert_hom_ref = revert_hom_ref
        self.output_suffix = output_suffix

        self.output_dir = dirname(output_file_name)
        self.output_prefix = splitext(output_file_name)[0]
        self.input_prefix_basename = basename(input_prefix)
        self.output_suffix = "" if not self.output_suffix else "." + self.output_suffix

    def run(self) -> tuple[str, str]:
        """
        Returns
        -------
        high_conf_calls_vcf, high_conf_concordance_vcf: Tuple[str, str]
        """

        combined_fn = self.input_prefix + ".vcf.gz"
        revert_fn = self.__revert_hom_ref(combined_fn)
        select_intervals_fn = self.__select_comparison_intervals(revert_fn)

        concordance_vcf = self.vcu.run_vcfeval_concordance(
            input_file=select_intervals_fn,
            truth_file=self.truth_file,
            output_prefix=self.output_prefix,
            ref_genome=self.ref_genome,
            sdf_index=self.sdf_index,
            evaluation_regions=str(self.highconf_intervals.as_bed_file()),
            comparison_intervals=self.cmp_intervals.as_bed_file(),
            input_sample=self.call_sample,
            truth_sample=self.truth_sample,
            ignore_filter=self.ignore_filter,
            mode="combine",
        )
        annotated_concordance_vcf = self.vu.annotate_tandem_repeats(concordance_vcf, self.ref_genome)

        high_conf_calls_vcf = select_intervals_fn.replace("vcf.gz", "highconf.vcf.gz")
        self.vu.intersect_with_intervals(
            select_intervals_fn, str(self.highconf_intervals.as_interval_list_file()), high_conf_calls_vcf
        )

        high_conf_concordance_vcf = annotated_concordance_vcf.replace("vcf.gz", "highconf.vcf.gz")

        self.vu.intersect_with_intervals(
            annotated_concordance_vcf, str(self.highconf_intervals.as_interval_list_file()), high_conf_concordance_vcf
        )
        return high_conf_calls_vcf, high_conf_concordance_vcf

    def __revert_hom_ref(self, combined_fn):
        revert_fn = pjoin(self.output_dir, self.input_prefix_basename + f"{self.output_suffix}.rev.hom.ref.vcf.gz")
        if self.revert_hom_ref:
            self.vcu.transform_hom_calls_to_het_calls(combined_fn, revert_fn)
        else:
            shutil.copy(combined_fn, revert_fn)
            shutil.copy(".".join((combined_fn, "tbi")), ".".join((revert_fn, "tbi")))
        return revert_fn

    def __select_comparison_intervals(self, revert_fn):
        select_intervals_fn = pjoin(self.output_dir, self.input_prefix_basename + f"{self.output_suffix}.intsct.vcf.gz")
        if not self.cmp_intervals.is_none():
            self.vu.intersect_with_intervals(
                revert_fn, str(self.cmp_intervals.as_interval_list_file()), select_intervals_fn
            )
        else:
            shutil.copy(revert_fn, select_intervals_fn)
            self.vu.index_vcf(select_intervals_fn)
        return select_intervals_fn
