"""
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
SPDX-License-Identifier: MIT-0
"""
from cfnlint.helpers import FUNCTIONS_SINGLE
from cfnlint.rules import CloudFormationLintRule, RuleMatch


class Configuration(CloudFormationLintRule):
    """Check if Outputs are configured correctly"""

    id = "E6001"
    shortdesc = "Outputs have appropriate properties"
    description = "Making sure the outputs are properly configured"
    source_url = "https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/outputs-section-structure.html"
    tags = ["outputs"]

    valid_keys = ["Value", "Export", "Description", "Condition"]

    # Map can be singular or multiple for this case we are going to skip
    valid_funcs = FUNCTIONS_SINGLE + ["Fn::FindInMap", "Fn::Transform"]

    def check_func(self, value, path):
        """Check that a value is using the correct functions"""
        matches = []
        if isinstance(value, dict):
            if len(value) == 1:
                for k in value.keys():
                    if k not in self.valid_funcs:
                        message = "{0} must use one of the functions {1}"
                        matches.append(
                            RuleMatch(
                                path, message.format("/".join(path), self.valid_funcs)
                            )
                        )
            else:
                message = "{0} must use one of the functions {1}"
                matches.append(
                    RuleMatch(path, message.format("/".join(path), self.valid_funcs))
                )
        elif isinstance(value, (list)):
            message = "{0} must be a string or one of the functions {1}"
            matches.append(
                RuleMatch(path, message.format("/".join(path), self.valid_funcs))
            )

        return matches

    def check_export(self, value, path):
        """Check export structure"""
        matches = []
        if isinstance(value, dict):
            if len(value) == 1:
                for k, v in value.items():
                    if k != "Name":
                        message = '{0} must be a an object of one with key "Name"'
                        matches.append(RuleMatch(path, message.format("/".join(path))))
                    else:
                        matches.extend(self.check_func(v, path[:] + ["Name"]))
            else:
                message = '{0} must be a an object of one with key "Name"'
                matches.append(RuleMatch(path, message.format("/".join(path))))
        else:
            message = '{0} must be a an object of one with key "Name"'
            matches.append(RuleMatch(path, message.format("/".join(path))))

        return matches

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

        outputs = cfn.template.get("Outputs", {})
        if outputs:
            if isinstance(outputs, dict):
                for output_name, output_value in outputs.items():
                    if not isinstance(output_value, dict):
                        message = "Output {0} is not an object"
                        matches.append(
                            RuleMatch(
                                ["Outputs", output_name], message.format(output_name)
                            )
                        )
                    else:
                        for propname, propvalue in output_value.items():
                            if propname not in self.valid_keys:
                                message = "Output {0} has invalid property {1}"
                                matches.append(
                                    RuleMatch(
                                        ["Outputs", output_name, propname],
                                        message.format(output_name, propname),
                                    )
                                )
                            else:
                                if propvalue is None:
                                    message = (
                                        "Output {0} has property {1} has invalid type"
                                    )
                                    matches.append(
                                        RuleMatch(
                                            ["Outputs", output_name, propname],
                                            message.format(output_name, propname),
                                        )
                                    )
                        value = output_value.get("Value")
                        if value:
                            matches.extend(
                                self.check_func(
                                    value, ["Outputs", output_name, "Value"]
                                )
                            )
                        export = output_value.get("Export")
                        if export:
                            matches.extend(
                                self.check_export(
                                    export, ["Outputs", output_name, "Export"]
                                )
                            )
            else:
                matches.append(
                    RuleMatch(["Outputs"], "Outputs do not follow correct format.")
                )

        return matches
