"""Terraform utilities"""
import hashlib
from typing import Any, Dict, List, Union

from samcli.hook_packages.terraform.hooks.prepare.types import (
    ConstantValue,
    ResolvedReference,
)
from samcli.lib.utils.hash import str_checksum

# max logical id len is 255
LOGICAL_ID_HASH_LEN = 8
LOGICAL_ID_MAX_HUMAN_LEN = 247


def build_cfn_logical_id(tf_address: str) -> str:
    """
    Builds a CloudFormation logical ID out of a terraform resource address

    Parameters
    ----------
    tf_address: str
        terraform resource address

    Returns
    -------
    str
        CloudFormation logical ID
    """
    # ignores non-alphanumericals, makes uppercase the first alphanumerical char and the
    # alphanumerical char right after a non-alphanumerical char
    chars: List[str] = []
    nextCharUppercase = True
    for char in tf_address:
        if len(chars) == LOGICAL_ID_MAX_HUMAN_LEN:
            break
        if not char.isalnum():
            nextCharUppercase = True
            continue
        if nextCharUppercase:
            chars.append(char.upper())
            nextCharUppercase = False
        else:
            chars.append(char)

    # Add a hash to avoid naming conflicts
    human_part = "".join(chars)
    hash_part = str_checksum(tf_address)[:LOGICAL_ID_HASH_LEN].upper()

    return human_part + hash_part


def _calculate_configuration_attribute_value_hash(
    configuration_attribute_value: Union[str, List[Union[ConstantValue, ResolvedReference]]]
) -> str:
    """
    Create a hash value of an attribute value of the resource configuration.

    Parameters
    ----------
    configuration_attribute_value: Union[str, List[Union[ConstantValue, ResolvedReference]]]
        An attribute value of the resource configuration. Its value can be either string if it can be resolved from
        the planned value section, or a list of references to other attributes.

    Returns
    -------
    str
        hash for the given object
    """
    md5 = hashlib.md5()

    if isinstance(configuration_attribute_value, str):
        md5.update(configuration_attribute_value.encode())
    else:
        sorted_references_list = sorted(
            configuration_attribute_value,
            key=lambda x: x.value if isinstance(x, ConstantValue) else f"{x.module_address}.{x.value}",  # type: ignore
        )
        for ref in sorted_references_list:
            md5.update(
                ref.value.encode() if isinstance(ref, ConstantValue) else f"{ref.module_address}.{ref.value}".encode()
            )
    return md5.hexdigest()


def get_sam_metadata_planned_resource_value_attribute(
    sam_metadata_resource_planned_values: Dict, attr_name: str
) -> Any:
    return sam_metadata_resource_planned_values.get("values", {}).get("triggers", {}).get(attr_name)
