import pytest

from pcluster.aws.common import AWSClientError
from pcluster.validators.s3_validators import S3BucketRegionValidator, S3BucketUriValidator, UrlValidator
from tests.pcluster.validators.utils import assert_failure_messages


@pytest.mark.parametrize(
    "url, expected_bucket_owner, response, expected_message, error, head_object_error",
    [
        ("s3://test/post_install.sh", None, True, None, None, None),
        (
            "s3://test/post_install.sh",
            "01234567890",
            None,
            "Failed when accessing object 's3://test/post_install.sh' from bucket 'test'. "
            "This can be due to bucket owner not matching the expected one '01234567890'",
            None,
            AWSClientError(
                function_name="head_object",
                message="Failed when accessing object 's3://test/post_install.sh' from bucket 'test'. "
                "This can be due to bucket owner not matching the expected one '01234567890'",
                error_code="403",
            ),
        ),
        ("s3://test/post_install.sh", "01234567890", True, None, None, None),
        ("s3://test/post_install.sh", None, True, None, None, None),
        ("https://test/cookbook.tgz", None, True, None, None, None),
        (
            "file:///test/node.tgz",
            None,
            False,
            "The value 'file:///test/node.tgz' is not a valid URL, " "choose URL with 'https' or 's3' prefix.",
            None,
            None,
        ),
        (
            "fake://test/cookbook.tgz",
            None,
            False,
            "The value 'fake://test/cookbook.tgz' is not a valid URL, " "choose URL with 'https' or 's3' prefix.",
            None,
            None,
        ),
        (
            "https://test/cookbook1.tgz",
            None,
            False,
            "The url 'https://test/cookbook1.tgz' causes ConnectionError",
            ConnectionError(),
            None,
        ),
        (
            "https://test/cookbook.tgz",
            "01234567890",
            False,
            "S3BucketOwner can only be specified with S3 URL",
            None,
            None,
        ),
    ],
)
def test_url_validator(
    mocker, url, expected_bucket_owner, response, expected_message, aws_api_mock, error, head_object_error
):
    aws_api_mock.s3.head_object.return_value = response
    aws_api_mock.s3.head_object.side_effect = head_object_error
    mocker.patch("pcluster.validators.s3_validators.urlopen", side_effect=error)

    actual_failures = UrlValidator().execute(url=url, expected_bucket_owner=expected_bucket_owner)
    assert_failure_messages(actual_failures, expected_message)


@pytest.mark.parametrize(
    "url, expected_message",
    [
        ("s3://test/test1/test2", None),
        ("http://test/test.json", "is not a valid S3 URI"),
    ],
)
def test_s3_bucket_uri_validator(mocker, url, expected_message, aws_api_mock):
    aws_api_mock.s3.head_bucket.return_value = True
    actual_failures = S3BucketUriValidator().execute(url=url)
    assert_failure_messages(actual_failures, expected_message)
    if url.startswith("s3://"):
        aws_api_mock.s3.head_bucket.assert_called()


@pytest.mark.parametrize(
    "bucket, bucket_region, cluster_region, expected_message",
    [
        ("bucket", "us-east-1", "us-east-1", None),
        ("bucket", "us-west-1", "us-west-1", None),
        ("bucket", "eu-west-1", "us-east-1", "cannot be used because it is not in the same region of the cluster."),
    ],
)
def test_s3_bucket_region_validator(mocker, bucket, bucket_region, cluster_region, expected_message, aws_api_mock):
    aws_api_mock.s3.get_bucket_region.return_value = bucket_region
    actual_failures = S3BucketRegionValidator().execute(bucket=bucket, region=cluster_region)
    assert_failure_messages(actual_failures, expected_message)
    aws_api_mock.s3.get_bucket_region.assert_called_with(bucket)
