import logging

import allure
import pytest
from frostfs_testlib.resources.wellknown_acl import PUBLIC_ACL
from frostfs_testlib.s3 import AwsCliClient, S3ClientWrapper
from frostfs_testlib.steps.cli.container import create_container
from frostfs_testlib.steps.cli.object import put_object_to_random_node
from frostfs_testlib.steps.http.http_gate import (
    assert_hashes_are_equal,
    get_object_by_attr_and_verify_hashes,
    get_via_http_gate,
    try_to_get_object_via_passed_request_and_expect_error,
    verify_object_hash,
)
from frostfs_testlib.steps.s3 import s3_helper
from frostfs_testlib.storage.dataclasses.object_size import ObjectSize
from frostfs_testlib.testing.cluster_test_base import ClusterTestBase
from frostfs_testlib.utils.file_utils import generate_file

logger = logging.getLogger("NeoLogger")


@pytest.mark.sanity
@pytest.mark.http_gate
class Test_http_object(ClusterTestBase):
    PLACEMENT_RULE = "REP 2 IN X CBF 1 SELECT 4 FROM * AS X"

    @pytest.fixture(scope="class", autouse=True)
    @allure.title("[Class/Autouse]: Prepare wallet and deposit")
    def prepare_wallet(self, default_wallet):
        Test_http_object.wallet = default_wallet

    @allure.title("Put over gRPC, Get over HTTP with attributes (obj_size={object_size})")
    def test_object_put_get_attributes(self, object_size: ObjectSize):
        """
        Test that object can be put using gRPC interface and got using HTTP.

        Steps:
        1. Create an object;
        2. Put object(s) using gRPC (frostfs-cli) with attributes [--attributes chapter1=peace,chapter2=war];
        3. Download the object using HTTP gate (https://git.frostfs.info/TrueCloudLab/frostfs-http-gw#downloading);
        4. Compare hashes of the original and the downloaded object;
        5. [Negative] Try to the get the object with the specified attributes and `get` request: [get/$CID/chapter1/peace];
        6. Download the object with the specified attributes and `get_by_attribute` request: [get_by_attribute/$CID/chapter1/peace];
        7. Compare hashes of the original and the downloaded object;
        8. [Negative] Try to the get the object via `get_by_attribute` request: [get_by_attribute/$CID/$OID];


        Expected result:
        Hashes must be the same.
        """

        with allure.step("Create public container"):
            cid = create_container(
                self.wallet,
                shell=self.shell,
                endpoint=self.cluster.default_rpc_endpoint,
                rule=self.PLACEMENT_RULE,
                basic_acl=PUBLIC_ACL,
            )

        # Generate file
        file_path = generate_file(object_size.value)

        # List of Key=Value attributes
        obj_key1 = "chapter1"
        obj_value1 = "peace"
        obj_key2 = "chapter2"
        obj_value2 = "war"

        # Prepare for grpc PUT request
        key_value1 = obj_key1 + "=" + obj_value1
        key_value2 = obj_key2 + "=" + obj_value2

        with allure.step("Put objects using gRPC [--attributes chapter1=peace,chapter2=war]"):
            oid = put_object_to_random_node(
                wallet=self.wallet,
                path=file_path,
                cid=cid,
                shell=self.shell,
                cluster=self.cluster,
                attributes=f"{key_value1},{key_value2}",
            )
        with allure.step("Get object and verify hashes [ get/$CID/$OID ]"):
            verify_object_hash(
                oid=oid,
                file_name=file_path,
                wallet=self.wallet,
                cid=cid,
                shell=self.shell,
                nodes=self.cluster.storage_nodes,
                endpoint=self.cluster.default_http_gate_endpoint,
                http_hostname=self.cluster.default_http_hostname[0],
            )

        with allure.step("[Negative] try to get object: [get/$CID/chapter1/peace]"):
            attrs = {obj_key1: obj_value1, obj_key2: obj_value2}
            request = f"/get/{cid}/{obj_key1}/{obj_value1}"
            expected_err_msg = "Failed to get object via HTTP gate:"
            try_to_get_object_via_passed_request_and_expect_error(
                cid=cid,
                oid=oid,
                error_pattern=expected_err_msg,
                http_request_path=request,
                attrs=attrs,
                endpoint=self.cluster.default_http_gate_endpoint,
                http_hostname=self.cluster.default_http_hostname[0],
            )

        with allure.step("Download the object with attribute [get_by_attribute/$CID/chapter1/peace]"):
            get_object_by_attr_and_verify_hashes(
                oid=oid,
                file_name=file_path,
                cid=cid,
                attrs=attrs,
                endpoint=self.cluster.default_http_gate_endpoint,
                http_hostname=self.cluster.default_http_hostname[0],
            )
        with allure.step("[Negative] try to get object: get_by_attribute/$CID/$OID"):
            request = f"/get_by_attribute/{cid}/{oid}"
            try_to_get_object_via_passed_request_and_expect_error(
                cid=cid,
                oid=oid,
                error_pattern=expected_err_msg,
                http_request_path=request,
                endpoint=self.cluster.default_http_gate_endpoint,
                http_hostname=self.cluster.default_http_hostname[0],
            )

    @allure.title("Put over s3, Get over HTTP with bucket name and key")
    @pytest.mark.parametrize("s3_client", [AwsCliClient], indirect=True)
    def test_object_put_get_bucketname_key(self, object_size: ObjectSize, s3_client: S3ClientWrapper):
        """
        Test that object can be put using s3-gateway interface and got via HTTP with bucket name and object key.

        Steps:
        1. Create an object;
        2. Create a bucket via s3;
        3. Put the object via s3;
        4. Download the object using HTTP gate with the bucket name and the object key;
        5. Compare hashes of the original and the downloaded objects;

        Expected result:
        Hashes must be the same.
        """

        file_path = generate_file(object_size.value)
        object_key = s3_helper.object_key_from_file_path(file_path)
        bucket = s3_client.create_bucket(acl="public-read-write")
        s3_client.put_object(bucket=bucket, filepath=file_path, key=object_key)
        obj_s3 = s3_client.get_object(bucket=bucket, key=object_key)

        request = f"/get/{bucket}/{object_key}"
        obj_http = get_via_http_gate(
            cid=None,
            oid=None,
            endpoint=self.cluster.default_http_gate_endpoint,
            http_hostname=self.cluster.default_http_hostname[0],
            request_path=request,
        )
        with allure.step("Verify hashes"):
            assert_hashes_are_equal(file_path, obj_http, obj_s3)