import logging
import re

from multiqc.base_module import BaseMultiqcModule, ModuleNoSamplesFound
from multiqc.plots import bargraph

log = logging.getLogger(__name__)


class MultiqcModule(BaseMultiqcModule):
    """
    The module reads the `*_peaks.xls` results files and prints the redundancy rates and number of peaks
    found in the General Statistics table. Numerous additional values are parsed and saved to
    `multiqc_data/multiqc_macs2.txt`.
    """

    def __init__(self):
        super(MultiqcModule, self).__init__(
            name="MACS2",
            anchor="macs",
            href="https://macs3-project.github.io/MACS/",
            info="Identifies transcription factor binding sites in ChIP-seq data.",
            extra="""
            MACS2 _(Model-based Analysis of ChIP-Seq)_ is a tool for identifying transcript
            factor binding sites. MACS captures the influence of genome complexity to
            evaluate the significance of enriched ChIP regions.""",
            doi=["10.1101/496521", "10.1186/gb-2008-9-9-r137"],
        )

        # Parse logs
        self.macs_data = dict()
        for f in self.find_log_files("macs2", filehandles=True):
            self.parse_macs(f)
            self.add_data_source(f)

        # Filter to strip out ignored sample names
        self.macs_data = self.ignore_samples(self.macs_data)

        if len(self.macs_data) == 0:
            raise ModuleNoSamplesFound

        log.info(f"Found {len(self.macs_data)} logs")
        self.write_data_file(self.macs_data, "multiqc_macs")

        self.macs_general_stats()
        self.macs_filtered_reads_plot()

    def parse_macs(self, f):
        regexes = {
            "name": r"# name = (.+)$",
            "fragment_size": r"# (?:fragment|tag) size is determined as (\d+) bps",
            "treatment_fragments_total": r"# total (?:fragments|tags) in treatment: (\d+)",
            "treatment_fragments_after_filtering": r"# (?:fragments|tags) after filtering in treatment: (\d+)",
            "treatment_max_duplicates": r"# maximum duplicate (?:fragments|tags at the same position) in treatment = (\d+)",
            "treatment_redundant_rate": r"# Redundant rate in treatment: ([\d\.]+)",
            "control_fragments_total": r"# total (?:fragments|tags) in control: (\d+)",
            "control_fragments_after_filtering": r"# (?:fragments|tags) after filtering in control: (\d+)",
            "control_max_duplicates": r"# maximum duplicate (?:fragments|tags at the same position) in control = (\d+)",
            "control_redundant_rate": r"# Redundant rate in control: ([\d\.]+)",
            "d": r"# d = (\d+)",
            "version": r"# This file is generated by MACS version ([\d\.]+)",
        }
        s_name = f["s_name"]
        parsed_data = {"peak_count": 0}
        version = None
        for line in f["f"]:
            line = line.strip()
            if line.startswith("#"):
                for k, r in regexes.items():
                    match = re.search(r, line)
                    if match:
                        if k == "name":
                            s_name = self.clean_s_name(match.group(1).strip(), f)
                        elif k == "version":
                            version = match.group(1)
                        else:
                            parsed_data[k] = float(match.group(1).strip())
            elif len(line) > 0 and "start" not in line:
                parsed_data["peak_count"] += 1

        if len(parsed_data) > 0:
            if s_name in self.macs_data:
                log.debug(f"Duplicate sample name found! Overwriting: {s_name}")
            self.macs_data[s_name] = parsed_data
            if version is not None:
                self.add_software_version(version, s_name)

    def macs_general_stats(self):
        """Add columns to General Statistics table"""
        headers = {
            "d": {"title": "Fragment Length", "min": 0, "format": "{:,.0f}"},
            "treatment_redundant_rate": {
                "title": "Treatment Redundancy",
                "description": "Redundant rate in treatment",
                "max": 1,
                "min": 0,
                "format": "{:,.2f}",
                "scale": "RdYlBu-rev",
            },
            "control_redundant_rate": {
                "title": "Control Redundancy",
                "description": "Redundant rate in control",
                "max": 1,
                "min": 0,
                "format": "{:,.2f}",
                "scale": "RdYlBu-rev",
            },
            "peak_count": {
                "title": "Number of Peaks",
                "description": "Total number of peaks",
                "min": 0,
                "format": "{:,.0f}",
            },
        }
        self.general_stats_addcols(self.macs_data, headers)

    def macs_filtered_reads_plot(self):
        """Plot of filtered reads for control and treatment samples"""
        data = dict()
        req_cats = [
            "control_fragments_total",
            "control_fragments_after_filtering",
            "treatment_fragments_total",
            "treatment_fragments_after_filtering",
        ]
        for s_name, d in self.macs_data.items():
            if all([c in d for c in req_cats]):
                data[f"{s_name}: Control"] = dict()
                data[f"{s_name}: Treatment"] = dict()
                data[f"{s_name}: Control"]["fragments_filtered"] = (
                    d["control_fragments_total"] - d["control_fragments_after_filtering"]
                )
                data[f"{s_name}: Control"]["fragments_not_filtered"] = d["control_fragments_after_filtering"]
                data[f"{s_name}: Treatment"]["fragments_filtered"] = (
                    d["treatment_fragments_total"] - d["treatment_fragments_after_filtering"]
                )
                data[f"{s_name}: Treatment"]["fragments_not_filtered"] = d["treatment_fragments_after_filtering"]

        # Check that we have something to plot
        if len(data) == 0:
            return

        # Specify the order of the different possible categories
        keys = {
            "fragments_not_filtered": {"color": "#437BB1", "name": "Remaining fragments"},
            "fragments_filtered": {"color": "#B1084C", "name": "Filtered fragments"},
        }

        # Config for the plot
        pconfig = {
            "id": "macs2_filtered",
            "title": "MACS2: Filtered Fragments",
            "ylab": "# Fragments",
            "cpswitch_counts_label": "Number of Fragments",
            "hide_empty": False,
        }

        self.add_section(plot=bargraph.plot(data, keys, pconfig))
