import boto3
import pytest
from moto import mock_s3
from uuid import uuid4


@mock_s3
class TestS3ObjectAttributes:
    def setup_method(self, *args) -> None:  # pylint: disable=unused-argument
        self.bucket_name = str(uuid4())
        self.s3 = boto3.resource("s3", region_name="us-east-1")
        self.client = boto3.client("s3", region_name="us-east-1")
        self.bucket = self.s3.Bucket(self.bucket_name)
        self.bucket.create()

        self.key = self.bucket.put_object(Key="mykey", Body=b"somedata")

    def test_get_etag(self):
        actual_etag = self.key.e_tag[1:-1]  # etag comes with quotes originally
        resp = self.client.get_object_attributes(
            Bucket=self.bucket_name, Key="mykey", ObjectAttributes=["ETag"]
        )

        headers = set(resp["ResponseMetadata"]["HTTPHeaders"].keys())
        assert "x-amz-version-id" not in headers
        assert "last-modified" in headers

        resp.pop("ResponseMetadata")

        assert set(resp.keys()) == {"ETag", "LastModified"}
        assert resp["ETag"] == actual_etag

    def test_get_attributes_storageclass(self):
        resp = self.client.get_object_attributes(
            Bucket=self.bucket_name, Key="mykey", ObjectAttributes=["StorageClass"]
        )

        resp.pop("ResponseMetadata")
        assert set(resp.keys()) == {"StorageClass", "LastModified"}
        assert resp["StorageClass"] == "STANDARD"

    def test_get_attributes_size(self):
        resp = self.client.get_object_attributes(
            Bucket=self.bucket_name, Key="mykey", ObjectAttributes=["ObjectSize"]
        )

        resp.pop("ResponseMetadata")
        assert set(resp.keys()) == {"ObjectSize", "LastModified"}
        assert resp["ObjectSize"] == 8

    @pytest.mark.parametrize(
        "algo_val",
        [
            ("CRC32", "6Le+Qw=="),
            ("SHA1", "hvfkN/qlp/zhXR3cuerq6jd2Z7g="),
            ("SHA256", "ypeBEsobvcr6wjGzmiPcTaeG7/gUfE5yuYB3ha/uSLs="),
        ],
    )
    def test_get_attributes_checksum(self, algo_val):
        algo, value = algo_val
        self.client.put_object(
            Bucket=self.bucket_name, Key="cs", Body="a", ChecksumAlgorithm=algo
        )

        resp = self.client.get_object_attributes(
            Bucket=self.bucket_name, Key="mykey", ObjectAttributes=["Checksum"]
        )
        resp.pop("ResponseMetadata")

        # Checksum is not returned, because it's not set
        assert set(resp.keys()) == {"LastModified"}

        # Retrieve checksum from key that was created with CRC32
        resp = self.client.get_object_attributes(
            Bucket=self.bucket_name, Key="cs", ObjectAttributes=["Checksum"]
        )

        resp.pop("ResponseMetadata")
        assert set(resp.keys()) == {"Checksum", "LastModified"}
        assert resp["Checksum"] == {f"Checksum{algo}": value}

    def test_get_attributes_multiple(self):
        resp = self.client.get_object_attributes(
            Bucket=self.bucket_name,
            Key="mykey",
            ObjectAttributes=["ObjectSize", "StorageClass"],
        )

        headers = set(resp["ResponseMetadata"]["HTTPHeaders"].keys())
        assert "x-amz-version-id" not in headers

        resp.pop("ResponseMetadata")
        assert set(resp.keys()) == {"ObjectSize", "LastModified", "StorageClass"}
        assert resp["ObjectSize"] == 8
        assert resp["StorageClass"] == "STANDARD"

    def test_get_versioned_object(self):
        self.bucket.Versioning().enable()
        key2 = self.bucket.put_object(Key="mykey", Body=b"moredata")

        resp = self.client.get_object_attributes(
            Bucket=self.bucket_name, Key="mykey", ObjectAttributes=["ETag"]
        )

        headers = resp["ResponseMetadata"]["HTTPHeaders"]
        header_keys = set(headers.keys())
        assert "x-amz-version-id" in header_keys
        assert headers["x-amz-version-id"] == key2.version_id

        resp.pop("ResponseMetadata")

        assert set(resp.keys()) == {"ETag", "LastModified", "VersionId"}
        assert resp["VersionId"] == key2.version_id
