#!/usr/bin/python3.9

"""
    This module contains keywords that utilize `neofs-cli container` commands.
"""

import json
import time
from typing import Optional

import json_transformers
from data_formatters import dict_to_attrs
from cli_helpers import _cmd_run
from common import NEOFS_ENDPOINT, NEOFS_CLI_EXEC, WALLET_CONFIG

from robot.api import logger
from robot.api.deco import keyword

ROBOT_AUTO_KEYWORDS = False
DEFAULT_PLACEMENT_RULE = "REP 2 IN X CBF 1 SELECT 4 FROM * AS X"

@keyword('Create Container')
def create_container(wallet: str, rule: str = DEFAULT_PLACEMENT_RULE, basic_acl: str = '',
                     attributes: Optional[dict] = None, session_token: str = '',
                     session_wallet: str = '', options: str = '') -> str:
    """
        A wrapper for `neofs-cli container create` call.

        Args:
            wallet (str): a wallet on whose behalf a container is created
            rule (optional, str): placement rule for container
            basic_acl (optional, str): an ACL for container, will be
                                appended to `--basic-acl` key
            attributes (optional, dict): container attributes , will be
                                appended to `--attributes` key
            session_token (optional, str): a path to session token file
            session_wallet(optional, str): a path to the wallet which signed
                                the session token; this parameter makes sense
                                when paired with `session_token`
            options (optional, str): any other options to pass to the call

        Returns:
            (str): CID of the created container
    """

    cmd = (
        f'{NEOFS_CLI_EXEC} --rpc-endpoint {NEOFS_ENDPOINT} container create '
        f'--wallet {session_wallet if session_wallet else wallet} '
        f'--config {WALLET_CONFIG} --policy "{rule}" '
        f'{"--basic-acl " + basic_acl if basic_acl else ""} '
        f'{"--attributes " + dict_to_attrs(attributes) if attributes else ""} '
        f'{"--session " + session_token if session_token else ""} '
        f'{options} --await'
    )
    output = _cmd_run(cmd, timeout=60)
    cid = _parse_cid(output)

    logger.info("Container created; waiting until it is persisted in sidechain")

    deadline_to_persist = 15  # seconds
    for i in range(0, deadline_to_persist):
        time.sleep(1)
        containers = list_containers(wallet)
        if cid in containers:
            break
        logger.info(f"There is no {cid} in {containers} yet; continue")
        if i + 1 == deadline_to_persist:
            raise RuntimeError(
                f"After {deadline_to_persist} seconds the container "
                f"{cid} hasn't been persisted; exiting"
            )
    return cid


@keyword('List Containers')
def list_containers(wallet: str) -> list[str]:
    """
        A wrapper for `neofs-cli container list` call. It returns all the
        available containers for the given wallet.
        Args:
            wallet (str): a wallet on whose behalf we list the containers
        Returns:
            (list): list of containers
    """
    cmd = (
        f'{NEOFS_CLI_EXEC} --rpc-endpoint {NEOFS_ENDPOINT} --wallet {wallet} '
        f'--config {WALLET_CONFIG} container list'
    )
    output = _cmd_run(cmd)
    return output.split()


@keyword('Get Container')
def get_container(wallet: str, cid: str, flag: str = '--json') -> dict:
    """
        A wrapper for `neofs-cli container get` call. It extracts container's
        attributes and rearranges them into a more compact view.
        Args:
            wallet (str): path to a wallet on whose behalf we get the container
            cid (str): ID of the container to get
            flag (str): output as json or plain text
        Returns:
            (dict, str): dict of container attributes
    """
    cmd = (
        f'{NEOFS_CLI_EXEC} --rpc-endpoint {NEOFS_ENDPOINT} --wallet {wallet} '
        f'--config {WALLET_CONFIG} --cid {cid} container get {flag}'
    )
    output = _cmd_run(cmd)
    if flag != '--json':
        return output
    container_info = json.loads(output)
    attributes = dict()
    for attr in container_info['attributes']:
        attributes[attr['key']] = attr['value']
    container_info['attributes'] = attributes
    container_info['ownerID'] = json_transformers.json_reencode(container_info['ownerID']['value'])
    return container_info


@keyword('Delete Container')
# TODO: make the error message about a non-found container more user-friendly
# https://github.com/nspcc-dev/neofs-contract/issues/121
def delete_container(wallet: str, cid: str) -> None:
    """
        A wrapper for `neofs-cli container delete` call.
        Args:
            wallet (str): path to a wallet on whose behalf we delete the container
            cid (str): ID of the container to delete
        This function doesn't return anything.
    """

    cmd = (
        f'{NEOFS_CLI_EXEC} --rpc-endpoint {NEOFS_ENDPOINT} --wallet {wallet} '
        f'--config {WALLET_CONFIG} container delete --cid {cid}'
    )
    _cmd_run(cmd)


def _parse_cid(output: str) -> str:
    """
    Parses container ID from a given CLI output. The input string we expect:
            container ID: 2tz86kVTDpJxWHrhw3h6PbKMwkLtBEwoqhHQCKTre1FN
            awaiting...
            container has been persisted on sidechain
    We want to take 'container ID' value from the string.

    Args:
        output (str): CLI output to parse

    Returns:
        (str): extracted CID
    """
    try:
        # taking first line from command's output
        first_line = output.split('\n')[0]
    except Exception:
        logger.error(f"Got empty output: {output}")
    splitted = first_line.split(": ")
    if len(splitted) != 2:
        raise ValueError(f"no CID was parsed from command output: \t{first_line}")
    return splitted[1]