# License: GNU Affero General Public License v3 or later
# A copy of GNU AGPL v3 should have been included in this software package in LICENSE.txt.

""" A module for analysing type II PKS clusters """

import logging
from typing import Any, Dict, List, Optional

from antismash.common import path
from antismash.config import ConfigType
from antismash.config.args import ModuleArgs
from antismash.common.secmet import Record
from antismash.common import hmmer

from .results import T2PKSResults
from .t2pks_analysis import analyse_cluster
from .html_output import generate_html, will_handle

NAME = "t2pks"
SHORT_DESCRIPTION = "type II PKS analysis"


def get_arguments() -> ModuleArgs:
    """ Constucts T2PKS module arguments
    """
    args = ModuleArgs("Advanced options", "t2pks", enabled_by_default=True)
    return args


def check_options(_options: ConfigType) -> List[str]:
    """ Check the options of this module for any conflicting or invalid values.

        Arguments:
            options: the options parsed by the main entry point as an
                     antismash.Config object

        Returns:
            a list of strings describing any errors, if they exist
    """
    return []


def prepare_data(logging_only: bool = False) -> List[str]:
    """ Ensures packaged data is fully prepared

        Arguments:
            logging_only: whether to return error messages instead of raising exceptions

        Returns:
            a list of error messages (only if logging_only is True)
    """
    database = path.get_full_path(__file__, "data", "t2pks.hmm")
    return hmmer.ensure_database_pressed(database, return_not_raise=logging_only)


def check_prereqs(options: ConfigType) -> List[str]:
    """ Check the prerequisites.
            hmmscan: domain detection
            blastp: CLF and starter unit analysis
            HMMs: t2pks.hmm

        Returns:
            a list of strings describing any errors, if they occurred
    """
    failure_messages = []
    for binary_name in ['hmmscan', "hmmpress", 'blastp']:
        if binary_name not in options.executables:
            failure_messages.append(f"Failed to locate executable: {binary_name}")

    for blastdb in ['KSIII', 'AT', 'LIG']:
        for ext in ['.fasta', '.phr', '.pin', '.psq']:
            dbfile = path.get_full_path(__file__, 'data', blastdb + ext)
            if path.locate_file(dbfile) is None:
                failure_messages.append(f"Failed to locate file: {dbfile}")

    failure_messages.extend(prepare_data(logging_only=True))

    return failure_messages


def is_enabled(options: ConfigType) -> bool:
    """ Returns True if the module is enabled by the given options """
    return options.t2pks_enabled or not options.minimal


def regenerate_previous_results(previous: Dict[str, Any], record: Record,
                                _options: ConfigType) -> Optional[T2PKSResults]:
    """ Rebuild the previous run results from a JSON object into this module's
        python results class. If the current options are incompatible with the
        previous results, None should be returned.

        Arguments:
            previous: the previous results as a dictionary
            record: the Record that was used to generate the previous results
            options: an antismash.Config object
    """
    return T2PKSResults.from_json(previous, record)


def run_on_record(record: Record, results: Optional[T2PKSResults], _options: ConfigType) -> T2PKSResults:
    """ Run this module's analysis section on the given record or use the
        previous results.

        Arguments:
            record: the Record instance to analyse
            results: the previous results as generated by regenerate_previous_results()
            options: an antismash.Config object

        Returns:
            this module's results as a subclass of
                antismash.common.module_results.ModuleResults
    """
    if results:
        return results

    results = T2PKSResults(record.id)

    t2pks_clusters = [cluster for cluster in record.get_protoclusters() if cluster.product == 'T2PKS']
    if not t2pks_clusters:
        logging.debug("No type II PKS clusters to analyze")
        return results

    logging.info("Anlysing type II PKS clusters")
    for cluster in t2pks_clusters:
        results.cluster_predictions[cluster.get_protocluster_number()] = analyse_cluster(cluster, record)

    return results
