"""
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
SPDX-License-Identifier: MIT-0
"""

import cfnlint.helpers
from cfnlint.helpers import REGISTRY_SCHEMAS
from cfnlint.rules import CloudFormationLintRule, RuleMatch
from cfnlint.template import Template


class Configuration(CloudFormationLintRule):
    """Check Base Resource Configuration"""

    id = "E3001"
    shortdesc = "Basic CloudFormation Resource Check"
    description = (
        "Making sure the basic CloudFormation resources are properly configured"
    )
    source_url = "https://github.com/aws-cloudformation/cfn-python-lint"
    tags = ["resources"]

    def _check_resource(self, cfn: Template, resource_name, resource_values):
        """Check Resource"""

        valid_attributes = [
            "Condition",
            "CreationPolicy",
            "DeletionPolicy",
            "DependsOn",
            "Metadata",
            "Properties",
            "Type",
            "UpdatePolicy",
            "UpdateReplacePolicy",
        ]

        valid_custom_attributes = [
            "Condition",
            "DeletionPolicy",
            "DependsOn",
            "Metadata",
            "Properties",
            "Type",
            "UpdateReplacePolicy",
            "Version",
        ]

        matches = []
        if not isinstance(resource_values, dict):
            message = "Resource not properly configured at {0}"
            matches.append(
                RuleMatch(["Resources", resource_name], message.format(resource_name))
            )
            return matches

        # validate condition is a string
        condition = resource_values.get("Condition", "")
        if not isinstance(condition, str):
            message = "Condition for resource {0} should be a string"
            matches.append(
                RuleMatch(
                    ["Resources", resource_name, "Condition"],
                    message.format(resource_name),
                )
            )

        resource_type = resource_values.get("Type", "")
        if not isinstance(resource_type, str):
            message = "Type has to be a string at {0}"
            matches.append(
                RuleMatch(
                    ["Resources", resource_name],
                    message.format("/".join(["Resources", resource_name])),
                )
            )
            return matches

        # Type is valid continue analysis
        if (
            resource_type.startswith("Custom::")
            or resource_type == "AWS::CloudFormation::CustomResource"
        ):
            check_attributes = valid_custom_attributes
        else:
            check_attributes = valid_attributes

        for property_key, _ in resource_values.items():
            if property_key not in check_attributes:
                message = "Invalid resource attribute {0} for resource {1}"
                matches.append(
                    RuleMatch(
                        ["Resources", resource_name, property_key],
                        message.format(property_key, resource_name),
                    )
                )

        if not resource_type:
            message = "Type not defined for resource {0}"
            matches.append(
                RuleMatch(["Resources", resource_name], message.format(resource_name))
            )
        elif not isinstance(resource_type, str):
            message = "Type has to be a string at {0}"
            matches.append(
                RuleMatch(
                    ["Resources", resource_name],
                    message.format("/".join(["Resources", resource_name])),
                )
            )
        else:
            self.logger.debug("Check resource types by region...")
            for region, specs in cfnlint.helpers.RESOURCE_SPECS.items():
                if region in cfn.regions:
                    if condition:
                        if False in cfn.conditions.build_scenerios_on_region(
                            condition, region
                        ):
                            continue
                    if resource_type not in specs[
                        "ResourceTypes"
                    ] and resource_type not in [
                        s["typeName"] for s in REGISTRY_SCHEMAS
                    ]:
                        if not resource_type.startswith(
                            ("Custom::", "AWS::Serverless::")
                        ) and not resource_type.endswith("::MODULE"):
                            message = "Invalid or unsupported Type {0} for resource {1} in {2}"
                            matches.append(
                                RuleMatch(
                                    ["Resources", resource_name, "Type"],
                                    message.format(
                                        resource_type, resource_name, region
                                    ),
                                )
                            )

        if "Properties" not in resource_values:
            resource_spec = cfnlint.helpers.RESOURCE_SPECS[cfn.regions[0]]
            if resource_type in resource_spec["ResourceTypes"]:
                properties_spec = resource_spec["ResourceTypes"][resource_type][
                    "Properties"
                ]
                # pylint: disable=len-as-condition
                if len(properties_spec) > 0:
                    required = 0
                    for _, property_spec in properties_spec.items():
                        if property_spec.get("Required", False):
                            required += 1
                    if required > 0:
                        if (
                            resource_type == "AWS::CloudFormation::WaitCondition"
                            and "CreationPolicy" in resource_values.keys()
                        ):
                            self.logger.debug(
                                "Exception to required properties section as CreationPolicy is defined."
                            )
                        else:
                            message = "Properties not defined for resource {0}"
                            matches.append(
                                RuleMatch(
                                    ["Resources", resource_name],
                                    message.format(resource_name),
                                )
                            )

        return matches

    def match(self, cfn):
        matches = []

        resources = cfn.template.get("Resources", {})
        if not isinstance(resources, dict):
            message = "Resource not properly configured"
            matches.append(RuleMatch(["Resources"], message))
        else:
            for resource_name, resource_values in cfn.template.get(
                "Resources", {}
            ).items():
                self.logger.debug(
                    "Validating resource %s base configuration", resource_name
                )
                matches.extend(
                    self._check_resource(cfn, resource_name, resource_values)
                )

        return matches
