from unittest import mock
from unittest.mock import patch

import botocore
from moto import mock_aws

from prowler.providers.aws.services.cloudfront.cloudfront_service import (
    CloudFront,
    DefaultCacheConfigBehaviour,
    Distribution,
    GeoRestrictionType,
    Origin,
    SSLSupportMethod,
    ViewerProtocolPolicy,
)
from tests.providers.aws.utils import AWS_REGION_US_EAST_1, set_mocked_aws_provider


def example_distribution_config(ref):
    """Return a basic example distribution config for use in tests."""
    return {
        "CallerReference": ref,
        "Origins": {
            "Quantity": 1,
            "Items": [
                {
                    "Id": "origin1",
                    "DomainName": "asdf.s3.us-east-1.amazonaws.com",
                    "S3OriginConfig": {"OriginAccessIdentity": ""},
                },
            ],
        },
        "OriginGroups": {
            "Quantity": 1,
            "Items": [
                {
                    "Id": "origin-group1",
                    "FailoverCriteria": {
                        "StatusCodes": {"Quantity": 1, "Items": [500]}
                    },
                    "Members": {
                        "Quantity": 2,
                        "Items": [
                            {
                                "OriginId": "origin1",
                            },
                            {
                                "OriginId": "origin2",
                            },
                        ],
                    },
                }
            ],
        },
        "DefaultCacheBehavior": {
            "TargetOriginId": "origin1",
            "ViewerProtocolPolicy": "allow-all",
            "MinTTL": 10,
            "ForwardedValues": {
                "QueryString": False,
                "Cookies": {"Forward": "none"},
            },
        },
        "ViewerCertificate": {
            "SSLSupportMethod": "static-ip",
            "Certificate": "arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012",
            "MinimumProtocolVersion": "TLSv1.3_2025",
        },
        "Comment": "an optional comment that's not actually optional",
        "Enabled": False,
    }


# Mocking Access Analyzer Calls
make_api_call = botocore.client.BaseClient._make_api_call


def mock_make_api_call(self, operation_name, kwarg):
    """
    As you can see the operation_name has the list_analyzers snake_case form but
    we are using the ListAnalyzers form.
    Rationale -> https://github.com/boto/botocore/blob/develop/botocore/client.py#L810:L816

    We have to mock every AWS API call using Boto3
    """
    if operation_name == "GetDistributionConfig":
        if kwarg["Id"]:
            return {
                "DistributionConfig": {
                    "Origins": {"Quantity": 123, "Items": []},
                    "OriginGroups": {"Quantity": 123, "Items": []},
                    "DefaultCacheBehavior": {
                        "TargetOriginId": "",
                        "TrustedSigners": {
                            "Enabled": False,
                            "Quantity": 123,
                            "Items": [
                                "",
                            ],
                        },
                        "TrustedKeyGroups": {
                            "Enabled": False,
                            "Quantity": 123,
                            "Items": [
                                "",
                            ],
                        },
                        "ViewerProtocolPolicy": "https-only",
                        "AllowedMethods": {
                            "Quantity": 123,
                            "Items": [
                                "GET",
                            ],
                            "CachedMethods": {
                                "Quantity": 123,
                                "Items": [
                                    "GET",
                                ],
                            },
                        },
                        "SmoothStreaming": False,
                        "Compress": False,
                        "LambdaFunctionAssociations": {},
                        "FunctionAssociations": {},
                        "FieldLevelEncryptionId": "enabled",
                        "RealtimeLogConfigArn": "test-log-arn",
                        "CachePolicyId": "",
                        "OriginRequestPolicyId": "",
                        "ResponseHeadersPolicyId": "",
                        "ForwardedValues": {
                            "QueryString": False,
                            "Cookies": {},
                            "Headers": {},
                            "QueryStringCacheKeys": {},
                        },
                        "MinTTL": 123,
                        "DefaultTTL": 123,
                        "MaxTTL": 123,
                    },
                    "CacheBehaviors": {},
                    "CustomErrorResponses": {},
                    "Comment": "",
                    "Logging": {
                        "Enabled": True,
                        "IncludeCookies": False,
                        "Bucket": "",
                        "Prefix": "",
                    },
                    "PriceClass": "PriceClass_All",
                    "Enabled": False,
                    "ViewerCertificate": {},
                    "Restrictions": {
                        "GeoRestriction": {
                            "RestrictionType": "blacklist",
                            "Quantity": 123,
                            "Items": [
                                "",
                            ],
                        }
                    },
                    "WebACLId": "test-web-acl",
                    "HttpVersion": "http2and3",
                    "IsIPV6Enabled": False,
                },
                "ETag": "",
            }
    if operation_name == "ListTagsForResource":
        return {
            "Tags": {
                "Items": [
                    {"Key": "test", "Value": "test"},
                ]
            }
        }
    if operation_name == "DescribeDeliverySources":
        return {"deliverySources": []}
    if operation_name == "DescribeDeliveries":
        return {"deliveries": []}
    return make_api_call(self, operation_name, kwarg)


# Patch every AWS call using Boto3
@patch("botocore.client.BaseClient._make_api_call", new=mock_make_api_call)
class Test_CloudFront_Service:
    # Test CloudFront Client
    @mock_aws
    def test_get_client(self):
        cloudfront = CloudFront(set_mocked_aws_provider())
        assert cloudfront.client.__class__.__name__ == "CloudFront"

    # Test CloudFront Session
    @mock_aws
    def test__get_session__(self):
        cloudfront = CloudFront(set_mocked_aws_provider())
        assert cloudfront.session.__class__.__name__ == "Session"

    # Test CloudFront Service
    @mock_aws
    def test__get_service__(self):
        cloudfront = CloudFront(set_mocked_aws_provider())
        assert cloudfront.service == "cloudfront"

    @mock_aws
    def test_list_distributionszero(self):
        cloudfront = CloudFront(set_mocked_aws_provider())

        assert len(cloudfront.distributions) == 0

    def test_list_distributionscomplete(self):
        from tests.providers.aws.utils import AWS_ACCOUNT_NUMBER

        DISTRIBUTION_ID = "E27LVI50CSW06W"
        DISTRIBUTION_ARN = (
            f"arn:aws:cloudfront::{AWS_ACCOUNT_NUMBER}:distribution/{DISTRIBUTION_ID}"
        )
        REGION = "us-east-1"
        LOGGING_ENABLED = True
        ORIGINS = [
            Origin(
                id="origin1",
                domain_name="asdf.s3.us-east-1.amazonaws.com",
                origin_protocol_policy="",
                origin_ssl_protocols=[],
            ),
        ]
        DEFAULT_CACHE_CONFIG = DefaultCacheConfigBehaviour(
            realtime_log_config_arn="test-log-arn",
            viewer_protocol_policy=ViewerProtocolPolicy.https_only,
            field_level_encryption_id="enabled",
        )
        GEO_RESTRICTION_TYPE = GeoRestrictionType.blacklist
        WEB_ACL_ID = "test-web-acl"
        TAGS = [
            {"Key": "test", "Value": "test"},
        ]
        SSL_SUPPORT_METHOD = SSLSupportMethod.sni_only
        CERTIFICATE = "arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012"
        MINIMUM_PROTOCOL_VERSION = "TLSv1.3_2025"

        cloudfront = mock.MagicMock
        cloudfront.distributions = {
            DISTRIBUTION_ID: Distribution(
                arn=DISTRIBUTION_ARN,
                id=DISTRIBUTION_ID,
                region=REGION,
                logging_enabled=LOGGING_ENABLED,
                origins=ORIGINS,
                default_cache_config=DEFAULT_CACHE_CONFIG,
                geo_restriction_type=GEO_RESTRICTION_TYPE,
                web_acl_id=WEB_ACL_ID,
                tags=TAGS,
                ssl_support_method=SSL_SUPPORT_METHOD,
                certificate=CERTIFICATE,
                minimum_protocol_version=MINIMUM_PROTOCOL_VERSION,
            )
        }

        assert len(cloudfront.distributions) == 1
        assert cloudfront.distributions[DISTRIBUTION_ID].arn == DISTRIBUTION_ARN
        assert cloudfront.distributions[DISTRIBUTION_ID].id == DISTRIBUTION_ID
        assert cloudfront.distributions[DISTRIBUTION_ID].region == AWS_REGION_US_EAST_1
        assert (
            cloudfront.distributions[DISTRIBUTION_ID].logging_enabled is LOGGING_ENABLED
        )
        for origin in cloudfront.distributions[DISTRIBUTION_ID].origins:
            assert origin.id == "origin1"
            assert origin.domain_name == "asdf.s3.us-east-1.amazonaws.com"
            assert origin.origin_protocol_policy == ""
            assert origin.origin_ssl_protocols == []
        assert (
            cloudfront.distributions[DISTRIBUTION_ID].geo_restriction_type
            == GEO_RESTRICTION_TYPE
        )
        assert cloudfront.distributions[DISTRIBUTION_ID].web_acl_id == "test-web-acl"
        assert (
            cloudfront.distributions[
                DISTRIBUTION_ID
            ].default_cache_config.realtime_log_config_arn
            == DEFAULT_CACHE_CONFIG.realtime_log_config_arn
        )
        assert (
            cloudfront.distributions[
                DISTRIBUTION_ID
            ].default_cache_config.viewer_protocol_policy
            == DEFAULT_CACHE_CONFIG.viewer_protocol_policy
        )
        assert (
            cloudfront.distributions[
                DISTRIBUTION_ID
            ].default_cache_config.field_level_encryption_id
            == DEFAULT_CACHE_CONFIG.field_level_encryption_id
        )
        assert cloudfront.distributions[DISTRIBUTION_ID].tags == TAGS
        assert (
            cloudfront.distributions[DISTRIBUTION_ID].minimum_protocol_version
            == MINIMUM_PROTOCOL_VERSION
        )

    def test_get_log_delivery_sources_with_active_delivery(self):
        from tests.providers.aws.utils import AWS_ACCOUNT_NUMBER

        DISTRIBUTION_ID = "E27LVI50CSW06W"
        DISTRIBUTION_ARN = (
            f"arn:aws:cloudfront::{AWS_ACCOUNT_NUMBER}:distribution/{DISTRIBUTION_ID}"
        )
        REGION = "us-east-1"

        distributions = {
            DISTRIBUTION_ID: Distribution(
                arn=DISTRIBUTION_ARN,
                id=DISTRIBUTION_ID,
                region=REGION,
                origins=[],
                origin_failover=False,
            )
        }

        mock_logs_client = mock.MagicMock()
        mock_paginator_sources = mock.MagicMock()
        mock_paginator_sources.paginate.return_value = [
            {
                "deliverySources": [
                    {
                        "name": "cf-source-1",
                        "service": "cloudfront",
                        "resourceArns": [DISTRIBUTION_ARN],
                    },
                ]
            }
        ]
        mock_paginator_deliveries = mock.MagicMock()
        mock_paginator_deliveries.paginate.return_value = [
            {
                "deliveries": [
                    {
                        "id": "delivery-1",
                        "deliverySourceName": "cf-source-1",
                        "deliveryDestinationArn": "arn:aws:logs:us-east-1:123456789012:log-group:cf-logs",
                    },
                ]
            }
        ]

        def paginator_side_effect(operation):
            if operation == "describe_delivery_sources":
                return mock_paginator_sources
            if operation == "describe_deliveries":
                return mock_paginator_deliveries

        mock_logs_client.get_paginator.side_effect = paginator_side_effect

        service = mock.MagicMock()
        service.service = "cloudfront"
        service.session.client.return_value = mock_logs_client

        CloudFront._get_log_delivery_sources(service, distributions, REGION)

        assert distributions[DISTRIBUTION_ID].logging_v2_enabled is True

    def test_get_log_delivery_sources_source_without_delivery(self):
        from tests.providers.aws.utils import AWS_ACCOUNT_NUMBER

        DISTRIBUTION_ID = "E27LVI50CSW06W"
        DISTRIBUTION_ARN = (
            f"arn:aws:cloudfront::{AWS_ACCOUNT_NUMBER}:distribution/{DISTRIBUTION_ID}"
        )
        REGION = "us-east-1"

        distributions = {
            DISTRIBUTION_ID: Distribution(
                arn=DISTRIBUTION_ARN,
                id=DISTRIBUTION_ID,
                region=REGION,
                origins=[],
                origin_failover=False,
            )
        }

        mock_logs_client = mock.MagicMock()
        mock_paginator_sources = mock.MagicMock()
        mock_paginator_sources.paginate.return_value = [
            {
                "deliverySources": [
                    {
                        "name": "cf-source-1",
                        "service": "cloudfront",
                        "resourceArns": [DISTRIBUTION_ARN],
                    },
                ]
            }
        ]
        mock_paginator_deliveries = mock.MagicMock()
        mock_paginator_deliveries.paginate.return_value = [{"deliveries": []}]

        def paginator_side_effect(operation):
            if operation == "describe_delivery_sources":
                return mock_paginator_sources
            if operation == "describe_deliveries":
                return mock_paginator_deliveries

        mock_logs_client.get_paginator.side_effect = paginator_side_effect

        service = mock.MagicMock()
        service.service = "cloudfront"
        service.session.client.return_value = mock_logs_client

        CloudFront._get_log_delivery_sources(service, distributions, REGION)

        assert distributions[DISTRIBUTION_ID].logging_v2_enabled is False

    def test_get_log_delivery_sources_no_matching_sources(self):
        from tests.providers.aws.utils import AWS_ACCOUNT_NUMBER

        DISTRIBUTION_ID = "E27LVI50CSW06W"
        DISTRIBUTION_ARN = (
            f"arn:aws:cloudfront::{AWS_ACCOUNT_NUMBER}:distribution/{DISTRIBUTION_ID}"
        )
        REGION = "us-east-1"

        distributions = {
            DISTRIBUTION_ID: Distribution(
                arn=DISTRIBUTION_ARN,
                id=DISTRIBUTION_ID,
                region=REGION,
                origins=[],
                origin_failover=False,
            )
        }

        mock_logs_client = mock.MagicMock()
        mock_paginator_sources = mock.MagicMock()
        mock_paginator_sources.paginate.return_value = [
            {
                "deliverySources": [
                    {
                        "name": "other-source",
                        "service": "VPC",
                        "resourceArns": ["arn:aws:some:other:resource"],
                    },
                ]
            }
        ]

        mock_logs_client.get_paginator.return_value = mock_paginator_sources

        service = mock.MagicMock()
        service.service = "cloudfront"
        service.session.client.return_value = mock_logs_client

        CloudFront._get_log_delivery_sources(service, distributions, REGION)

        assert distributions[DISTRIBUTION_ID].logging_v2_enabled is False
