from pathlib import Path
from typing import Dict, List

import yaml

from nf_core.pipelines.lint_utils import ignore_file


def multiqc_config(self) -> Dict[str, List[str]]:
    """Make sure basic multiQC plugins are installed and plots are exported
    Basic template:

    .. code-block:: yaml

        report_comment: >
            This report has been generated by the <a href="https://github.com/nf-core/quantms" target="_blank">nf-core/quantms</a>
            analysis pipeline. For information about how to interpret these results, please see the
            <a href="https://nf-co.re/quantms" target="_blank">documentation</a>.
        report_section_order:
            software_versions:
                order: -1000
            nf-core-quantms-summary:
                order: -1001
        export_plots: true

    .. note:: You can choose to ignore this lint tests by editing the file called
        ``.nf-core.yml`` in the root of your pipeline and setting the test to false:

        .. code-block:: yaml

            lint:
                multiqc_config: False

        To disable this test only for specific sections, you can specify a list of section names.
        For example:

        .. code-block:: yaml
        lint:
                multiqc_config:
                    - report_section_order
                    - report_comment

    """

    passed: List[str] = []
    failed: List[str] = []
    ignored: List[str] = []

    fn = Path(self.wf_path, "assets", "multiqc_config.yml")
    file_path = fn.relative_to(self.wf_path)
    passed, failed, ignored, ignore_configs = ignore_file("multiqc_config", file_path, self.wf_path)

    # skip other tests if the file is not found
    error_message = f"`{file_path}` not found"
    # check for partial match in failed or ignored
    if not any(f.startswith(error_message) for f in (failed + ignored)):
        try:
            with open(fn) as fh:
                mqc_yml = yaml.safe_load(fh)
        except Exception as e:
            return {"failed": [f"Could not parse yaml file: {fn}, {e}"]}

        # check if required sections are present
        required_sections = ["report_section_order", "export_plots", "report_comment"]
        for section in required_sections:
            if section not in mqc_yml and section not in ignore_configs:
                failed.append(f"`assets/multiqc_config.yml` does not contain `{section}`")
                return {"passed": passed, "failed": failed}
            else:
                passed.append(f"`assets/multiqc_config.yml` contains `{section}`")

        try:
            orders = {}
            summary_plugin_name = f"{self.pipeline_prefix}-{self.pipeline_name}-summary"
            min_plugins = ["software_versions", summary_plugin_name]
            for plugin in min_plugins:
                if plugin not in mqc_yml["report_section_order"]:
                    raise AssertionError(f"Section {plugin} missing in report_section_order")
                if "order" not in mqc_yml["report_section_order"][plugin]:
                    raise AssertionError(f"Section {plugin} 'order' missing. Must be < 0")
                plugin_order = mqc_yml["report_section_order"][plugin]["order"]
                if plugin_order >= 0:
                    raise AssertionError(f"Section {plugin} 'order' must be < 0")

            for plugin in mqc_yml["report_section_order"]:
                if "order" in mqc_yml["report_section_order"][plugin]:
                    orders[plugin] = mqc_yml["report_section_order"][plugin]["order"]

            if orders[summary_plugin_name] != min(orders.values()):
                raise AssertionError(f"Section {summary_plugin_name} should have the lowest order")
            orders.pop(summary_plugin_name)
            if orders["software_versions"] != min(orders.values()):
                raise AssertionError("Section software_versions should have the second lowest order")
        except (AssertionError, KeyError, TypeError) as e:
            failed.append(f"`assets/multiqc_config.yml` does not meet requirements: {e}")
        else:
            passed.append("`assets/multiqc_config.yml` follows the ordering scheme of the minimally required plugins.")

        if "report_comment" not in ignore_configs:
            # Check that the minimum plugins exist and are coming first in the summary
            version = self.nf_config.get("manifest.version", "").strip(" '\"")
            if "dev" in version:
                version = "dev"
                report_comments = (
                    f'This report has been generated by the <a href="https://github.com/nf-core/{self.pipeline_name}/tree/dev" target="_blank">nf-core/{self.pipeline_name}</a>'
                    f" analysis pipeline. For information about how to interpret these results, please see the "
                    f'<a href="https://nf-co.re/{self.pipeline_name}/dev/docs/output" target="_blank">documentation</a>.'
                )

            else:
                report_comments = (
                    f'This report has been generated by the <a href="https://github.com/nf-core/{self.pipeline_name}/releases/tag/{version}" target="_blank">nf-core/{self.pipeline_name}</a>'
                    f" analysis pipeline. For information about how to interpret these results, please see the "
                    f'<a href="https://nf-co.re/{self.pipeline_name}/{version}/docs/output" target="_blank">documentation</a>.'
                )

            if mqc_yml["report_comment"].strip() != report_comments:
                # find where the report_comment is wrong and give it as a hint
                hint = report_comments
                failed.append(
                    f"`assets/multiqc_config.yml` does not contain a matching 'report_comment'.  \n"
                    f"The expected comment is:  \n"
                    f"```{hint}```  \n"
                    f"The current comment is:  \n"
                    f"```{ mqc_yml['report_comment'].strip()}```"
                )
            else:
                passed.append("`assets/multiqc_config.yml` contains a matching 'report_comment'.")

        # Check that export_plots is activated
        try:
            if not mqc_yml["export_plots"]:
                raise AssertionError()
        except (AssertionError, KeyError, TypeError):
            failed.append("`assets/multiqc_config.yml` does not contain 'export_plots: true'.")
        else:
            passed.append("`assets/multiqc_config.yml` contains 'export_plots: true'.")

    return {"passed": passed, "failed": failed, "ignored": ignored}
