"""
    This module contains keywords for work with Storage Groups.
    It contains wrappers for `frostfs-cli storagegroup` verbs.
"""
import logging
from typing import Optional

import allure
from frostfs_testlib.cli import FrostfsCli
from frostfs_testlib.shell import Shell

from pytest_tests.helpers.cluster import Cluster
from pytest_tests.helpers.complex_object_actions import get_link_object
from pytest_tests.helpers.frostfs_verbs import head_object
from pytest_tests.resources.common import CLI_DEFAULT_TIMEOUT, FROSTFS_CLI_EXEC, WALLET_CONFIG

logger = logging.getLogger("NeoLogger")


@allure.step("Put Storagegroup")
def put_storagegroup(
    shell: Shell,
    endpoint: str,
    wallet: str,
    cid: str,
    objects: list,
    bearer: Optional[str] = None,
    wallet_config: str = WALLET_CONFIG,
    lifetime: int = 10,
    timeout: Optional[str] = CLI_DEFAULT_TIMEOUT,
) -> str:
    """
    Wrapper for `frostfs-cli storagegroup put`. Before the SG is created,
    frostfs-cli performs HEAD on `objects`, so this verb must be allowed
    for `wallet` in `cid`.
    Args:
        shell: Shell instance.
        wallet: Path to wallet on whose behalf the SG is created.
        cid: ID of Container to put SG to.
        lifetime: Storage group lifetime in epochs.
        objects: List of Object IDs to include into the SG.
        bearer: Path to Bearer token file.
        wallet_config: Path to frostfs-cli config file.
        timeout: Timeout for an operation.
    Returns:
        Object ID of created Storage Group.
    """
    frostfscli = FrostfsCli(
        shell=shell, frostfs_cli_exec_path=FROSTFS_CLI_EXEC, config_file=wallet_config
    )
    result = frostfscli.storagegroup.put(
        wallet=wallet,
        cid=cid,
        lifetime=lifetime,
        members=objects,
        bearer=bearer,
        rpc_endpoint=endpoint,
    )
    gid = result.stdout.split("\n")[1].split(": ")[1]
    return gid


@allure.step("List Storagegroup")
def list_storagegroup(
    shell: Shell,
    endpoint: str,
    wallet: str,
    cid: str,
    bearer: Optional[str] = None,
    wallet_config: str = WALLET_CONFIG,
    timeout: Optional[str] = CLI_DEFAULT_TIMEOUT,
) -> list:
    """
    Wrapper for `frostfs-cli storagegroup list`.  This operation
    requires SEARCH allowed for `wallet` in `cid`.
    Args:
        shell: Shell instance.
        wallet: Path to wallet on whose behalf the SGs are listed in the container
        cid: ID of Container to list.
        bearer: Path to Bearer token file.
        wallet_config: Path to frostfs-cli config file.
        timeout: Timeout for an operation.
    Returns:
        Object IDs of found Storage Groups.
    """
    frostfscli = FrostfsCli(
        shell=shell, frostfs_cli_exec_path=FROSTFS_CLI_EXEC, config_file=wallet_config
    )
    result = frostfscli.storagegroup.list(
        wallet=wallet,
        cid=cid,
        bearer=bearer,
        rpc_endpoint=endpoint,
        timeout=timeout,
    )
    # throwing off the first string of output
    found_objects = result.stdout.split("\n")[1:]
    return found_objects


@allure.step("Get Storagegroup")
def get_storagegroup(
    shell: Shell,
    endpoint: str,
    wallet: str,
    cid: str,
    gid: str,
    bearer: str = "",
    wallet_config: str = WALLET_CONFIG,
    timeout: Optional[str] = CLI_DEFAULT_TIMEOUT,
) -> dict:
    """
    Wrapper for `frostfs-cli storagegroup get`.
    Args:
        shell: Shell instance.
        wallet: Path to wallet on whose behalf the SG is got.
        cid: ID of Container where SG is stored.
        gid: ID of the Storage Group.
        bearer: Path to Bearer token file.
        wallet_config: Path to frostfs-cli config file.
        timeout: Timeout for an operation.
    Returns:
        Detailed information on the Storage Group.
    """
    frostfscli = FrostfsCli(
        shell=shell, frostfs_cli_exec_path=FROSTFS_CLI_EXEC, config_file=wallet_config
    )
    result = frostfscli.storagegroup.get(
        wallet=wallet,
        cid=cid,
        bearer=bearer,
        id=gid,
        rpc_endpoint=endpoint,
        timeout=timeout,
    )

    # TODO: temporary solution for parsing output. Needs to be replaced with
    # JSON parsing when https://github.com/nspcc-dev/frostfs-node/issues/1355
    # is done.
    strings = result.stdout.strip().split("\n")
    # first three strings go to `data`;
    # skip the 'Members:' string;
    # the rest of strings go to `members`
    data, members = strings[:3], strings[3:]
    sg_dict = {}
    for i in data:
        key, val = i.split(": ")
        sg_dict[key] = val
    sg_dict["Members"] = []
    for member in members[1:]:
        sg_dict["Members"].append(member.strip())
    return sg_dict


@allure.step("Delete Storagegroup")
def delete_storagegroup(
    shell: Shell,
    endpoint: str,
    wallet: str,
    cid: str,
    gid: str,
    bearer: str = "",
    wallet_config: str = WALLET_CONFIG,
    timeout: Optional[str] = CLI_DEFAULT_TIMEOUT,
) -> str:
    """
    Wrapper for `frostfs-cli storagegroup delete`.
    Args:
        shell: Shell instance.
        wallet: Path to wallet on whose behalf the SG is deleted.
        cid: ID of Container where SG is stored.
        gid: ID of the Storage Group.
        bearer: Path to Bearer token file.
        wallet_config: Path to frostfs-cli config file.
        timeout: Timeout for an operation.
    Returns:
        Tombstone ID of the deleted Storage Group.
    """
    frostfscli = FrostfsCli(
        shell=shell, frostfs_cli_exec_path=FROSTFS_CLI_EXEC, config_file=wallet_config
    )
    result = frostfscli.storagegroup.delete(
        wallet=wallet,
        cid=cid,
        bearer=bearer,
        id=gid,
        rpc_endpoint=endpoint,
        timeout=timeout,
    )
    tombstone_id = result.stdout.strip().split("\n")[1].split(": ")[1]
    return tombstone_id


@allure.step("Verify list operation over Storagegroup")
def verify_list_storage_group(
    shell: Shell,
    endpoint: str,
    wallet: str,
    cid: str,
    gid: str,
    bearer: str = None,
    wallet_config: str = WALLET_CONFIG,
    timeout: Optional[str] = CLI_DEFAULT_TIMEOUT,
):
    storage_groups = list_storagegroup(
        shell=shell,
        endpoint=endpoint,
        wallet=wallet,
        cid=cid,
        bearer=bearer,
        wallet_config=wallet_config,
        timeout=timeout,
    )
    assert gid in storage_groups


@allure.step("Verify get operation over Storagegroup")
def verify_get_storage_group(
    shell: Shell,
    cluster: Cluster,
    wallet: str,
    cid: str,
    gid: str,
    obj_list: list,
    object_size: int,
    max_object_size: int,
    bearer: str = None,
    wallet_config: str = WALLET_CONFIG,
    timeout: Optional[str] = CLI_DEFAULT_TIMEOUT,
):
    obj_parts = []
    endpoint = cluster.default_rpc_endpoint
    if object_size > max_object_size:
        for obj in obj_list:
            link_oid = get_link_object(
                wallet,
                cid,
                obj,
                shell=shell,
                nodes=cluster.storage_nodes,
                bearer=bearer,
                wallet_config=wallet_config,
                timeout=timeout,
            )
            obj_head = head_object(
                wallet=wallet,
                cid=cid,
                oid=link_oid,
                shell=shell,
                endpoint=endpoint,
                is_raw=True,
                bearer=bearer,
                wallet_config=wallet_config,
                timeout=timeout,
            )
            obj_parts = obj_head["header"]["split"]["children"]

    obj_num = len(obj_list)
    storagegroup_data = get_storagegroup(
        shell=shell,
        endpoint=endpoint,
        wallet=wallet,
        cid=cid,
        gid=gid,
        bearer=bearer,
        wallet_config=wallet_config,
        timeout=timeout,
    )
    exp_size = object_size * obj_num
    if object_size < max_object_size:
        assert int(storagegroup_data["Group size"]) == exp_size
        assert storagegroup_data["Members"] == obj_list
    else:
        assert int(storagegroup_data["Group size"]) == exp_size
        assert storagegroup_data["Members"] == obj_parts