"""
Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
SPDX-License-Identifier: MIT-0
"""
import logging
from typing import List, Optional, Sequence, Union

from cfnlint.graph import Graph
from cfnlint.rules import Match, RulesCollection
from cfnlint.template import Template
from cfnlint.transform import Transform

LOGGER = logging.getLogger(__name__)


class Runner:
    """Run all the rules"""

    def __init__(
        self,
        rules: RulesCollection,
        filename: Optional[str],
        template: str,
        regions: Sequence[str],
        verbosity=0,
        mandatory_rules: Union[Sequence[str], None] = None,
    ):
        self.rules = rules
        self.filename = filename
        self.verbosity = verbosity
        self.mandatory_rules = mandatory_rules or []
        self.cfn = Template(filename, template, regions)

    def transform(self):
        """Transform logic"""
        LOGGER.debug("Transform templates if needed")
        sam_transform = "AWS::Serverless-2016-10-31"
        matches = []
        transform_declaration = self.cfn.template.get("Transform", [])
        transform_type = (
            transform_declaration
            if isinstance(transform_declaration, list)
            else [transform_declaration]
        )
        # Don't call transformation if Transform is not specified to prevent
        # useless execution of the transformation.
        # Currently locked in to SAM specific
        if sam_transform not in transform_type:
            return matches
        # Save the Globals section so its available for rule processing
        self.cfn.transform_pre["Globals"] = self.cfn.template.get("Globals", {})
        transform = Transform(self.filename, self.cfn.template, self.cfn.regions[0])
        matches = transform.transform_template()
        self.cfn.template = transform.template()
        self.cfn.graph = Graph(self.cfn)
        return matches

    def run(self) -> List[Match]:
        """Run rules"""
        LOGGER.info("Run scan of template %s", self.filename)
        matches = []
        if self.cfn.template is not None:
            matches.extend(self.rules.run(self.filename, self.cfn))
        return self.check_metadata_directives(matches)

    def check_metadata_directives(self, matches: Sequence[Match]) -> List[Match]:
        # uniq the list of incidents and filter out exceptions from the template
        directives = self.cfn.get_directives()
        return_matches: List[Match] = []
        for match in matches:
            if not any(match == u for u in return_matches):
                if match.rule.id not in directives:
                    return_matches.append(match)
                else:
                    for mandatory_rule in self.mandatory_rules:
                        if match.rule.id.startswith(mandatory_rule):
                            return_matches.append(match)
                            break
                    else:
                        for directive in directives.get(match.rule.id):
                            start = directive.get("start")
                            end = directive.get("end")
                            if start[0] < match.linenumber < end[0]:
                                break
                            if (
                                start[0] == match.linenumber
                                and start[1] <= match.columnnumber
                            ):
                                break
                            if (
                                end[0] == match.linenumber
                                and end[1] >= match.columnnumberend
                            ):
                                break
                        else:
                            return_matches.append(match)

        return return_matches
