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


class Elb(CloudFormationLintRule):
    """Check if Elb Resource Properties"""

    id = 'E2503'
    shortdesc = 'Resource ELB Properties'
    description = 'See if Elb Resource Properties are set correctly \
HTTPS has certificate HTTP has no certificate'
    source_url = 'https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-elb-listener.html'
    tags = ['properties', 'elb']

    def __init__(self):
        """Init"""
        super().__init__()
        self.resource_property_types = ['AWS::ElasticLoadBalancingV2::LoadBalancer']

    def check_protocol_value(self, value, path, **kwargs):
        """
        Check Protocol Value
        """
        matches = []
        if isinstance(value, str):
            if value.upper() not in kwargs['accepted_protocols']:
                message = 'Protocol must be {0} is invalid at {1}'
                matches.append(
                    RuleMatch(
                        path,
                        message.format(
                            (', '.join(kwargs['accepted_protocols'])),
                            ('/'.join(map(str, path))),
                        ),
                    )
                )
            elif value.upper() in kwargs['certificate_protocols']:
                if not kwargs['certificates']:
                    message = (
                        'Certificates should be specified when using HTTPS for {0}'
                    )
                    matches.append(
                        RuleMatch(path, message.format(('/'.join(map(str, path)))))
                    )

        return matches

    def get_loadbalancer_type(self, properties):
        """Check if type is application"""
        elb_type = properties.get('Type', 'application')
        if isinstance(elb_type, str):
            if elb_type == 'application':
                return 'application'
            return 'network'
        return None

    def check_alb_subnets(self, properties, path, scenario):
        """Validate at least two subnets with ALBs"""
        matches = []
        if self.get_loadbalancer_type(properties) == 'application':
            subnets = properties.get('Subnets')
            if isinstance(subnets, list):
                if len(subnets) < 2:
                    if scenario:
                        message = 'You must specify at least two Subnets for load balancers with type "application" {0}'
                        scenario_text = ' and '.join(
                            [
                                f'when condition "{k}" is {v}'
                                for (k, v) in scenario.items()
                            ]
                        )
                        matches.append(RuleMatch(path, message.format(scenario_text)))
                    else:
                        matches.append(
                            RuleMatch(
                                path[:] + ['Subnets'],
                                'You must specify at least two Subnets for load balancers with type "application"',
                            )
                        )
            subnet_mappings = properties.get('SubnetMappings')
            if isinstance(subnet_mappings, list):
                if len(subnet_mappings) < 2:
                    if scenario:
                        message = 'You must specify at least two SubnetMappings for load balancers with type "application" {0}'
                        scenario_text = ' and '.join(
                            [
                                f'when condition "{k}" is {v}'
                                for (k, v) in scenario.items()
                            ]
                        )
                        matches.append(RuleMatch(path, message.format(scenario_text)))
                    else:
                        matches.append(
                            RuleMatch(
                                path[:] + ['SubnetMappings'],
                                'You must specify at least two SubnetMappings for load balancers with type "application"',
                            )
                        )

        return matches

    def match(self, cfn):
        """Check ELB Resource Parameters"""

        matches = []

        results = cfn.get_resource_properties(['AWS::ElasticLoadBalancingV2::Listener'])
        for result in results:
            matches.extend(
                cfn.check_value(
                    result['Value'],
                    'Protocol',
                    result['Path'],
                    check_value=self.check_protocol_value,
                    accepted_protocols=[
                        'GENEVE',
                        'HTTP',
                        'HTTPS',
                        'TCP',
                        'TCP_UDP',
                        'TLS',
                        'UDP',
                    ],
                    certificate_protocols=['HTTPS', 'TLS'],
                    certificates=result['Value'].get('Certificates'),
                )
            )

        results = cfn.get_resource_properties(
            ['AWS::ElasticLoadBalancing::LoadBalancer', 'Listeners']
        )
        for result in results:
            if isinstance(result['Value'], list):
                for index, listener in enumerate(result['Value']):
                    matches.extend(
                        cfn.check_value(
                            listener,
                            'Protocol',
                            result['Path'] + [index],
                            check_value=self.check_protocol_value,
                            accepted_protocols=['HTTP', 'HTTPS', 'TCP', 'SSL'],
                            certificate_protocols=['HTTPS', 'SSL'],
                            certificates=listener.get('SSLCertificateId'),
                        )
                    )

        return matches

    def match_resource_properties(self, resource_properties, _, path, cfn):
        """Check Load Balancers"""
        matches = []

        # Play out conditions to determine the relationship between property values
        scenarios = cfn.get_object_without_nested_conditions(resource_properties, path)
        for scenario in scenarios:
            properties = scenario.get('Object')
            if self.get_loadbalancer_type(properties) == 'network':
                if properties.get('SecurityGroups'):
                    if scenario.get('Scenario'):
                        scenario_text = ' and '.join(
                            [
                                f'when condition "{k}" is {v}'
                                for (k, v) in scenario.get('Scenario').items()
                            ]
                        )
                        message = f'Security groups are not supported for load balancers with type "network" {scenario_text}'
                        matches.append(RuleMatch(path, message))
                    else:
                        path = path + ['SecurityGroups']
                        matches.append(
                            RuleMatch(
                                path,
                                'Security groups are not supported for load balancers with type "network"',
                            )
                        )

            matches.extend(
                self.check_alb_subnets(properties, path, scenario.get('Scenario'))
            )

        return matches
