import os
import re
import time
import exrex
import hashlib


# ==-----------------------------------------------------------------------------== #
# Cryptography                                                                      #
# ==-----------------------------------------------------------------------------== #
def md5(string: str) -> str:
    """Hash string using `md5` algorithm."""

    return hashlib.md5(string.encode()).hexdigest()


def sha1(string: str) -> str:
    """Hash string using `sha1` algorithm."""

    return hashlib.sha1(string.encode()).hexdigest()


def sha224(string: str) -> str:
    """Hash string using `sha224` algorithm."""

    return hashlib.sha224(string.encode()).hexdigest()


def sha256(string: str) -> str:
    """Hash string using `sha256` algorithm."""

    return hashlib.sha256(string.encode()).hexdigest()


def sha384(string: str) -> str:
    """Hash string using `sha384` algorithm."""

    return hashlib.sha384(string.encode()).hexdigest()


def sha512(string: str) -> str:
    """Hash string using `sha512` algorithm."""

    return hashlib.sha512(string.encode()).hexdigest()


# ==-----------------------------------------------------------------------------== #
# String functions                                                                  #
# ==-----------------------------------------------------------------------------== #
# NOTE: This function is required to make able to use `REGEXP` keyword at SQLite syntax.
# Looks like, SQLite doesn't have built in engine to execute it itself... i dunno ¯\_(ツ)_/¯
def regexp(pattern: str, string: str) -> bool:
    """Check if string matches given regexp pattern."""

    return re.match(pattern, string) is not None


def random_regexp_string(pattern: str) -> str:
    """Generate random string, that matches given regexp pattern."""

    if not isinstance(result := exrex.getone(pattern), str):
        raise Exception("Unable to generate string according given pattern")

    return result


def contains(string: str, substring: str) -> bool:
    """Check if substring is contains in string."""

    return substring in string


def startswith(string: str, substring: str) -> bool:
    """Check if string starts with substring."""

    return string.startswith(substring)


def endswith(string: str, substring: str) -> bool:
    """Check if string ends with substring."""

    return string.endswith(substring)


def reverse(string: str) -> str:
    """Reverse string."""

    return string[::-1]


def replace(string: str, old: str, new: str, times: int) -> str:
    """Replace substring in in string N times. Replace all substring if `times` set to `-1`."""

    return string.replace(old, new, times)


def strip(string: str) -> str:
    """Remove all escape and spaces sequences from the start and the end of the string."""

    return str(string).strip()


def to_lower(string: str) -> str:
    """Convert string to lowercase."""

    return string.lower()


def to_upper(string: str) -> str:
    """Convert string to upercase."""

    return string.upper()


def to_capital(string: str) -> str:
    """Convert first char of the string to uppercase, rest chars to lowercase."""

    return string.capitalize()


def uuid7() -> str:
    """Generate UUIDv7 according RFC 9562."""

    # Generate random byte sequence
    random_bytes = bytearray(os.urandom(16))

    # Retrieve current time in milliseconds
    current_time = int(time.time() * 1000)

    # Pack UUID struct
    random_bytes[0] = (current_time >> 40) & 0xFF
    random_bytes[1] = (current_time >> 32) & 0xFF
    random_bytes[2] = (current_time >> 24) & 0xFF
    random_bytes[3] = (current_time >> 16) & 0xFF
    random_bytes[4] = (current_time >> 8) & 0xFF
    random_bytes[5] = (current_time >> 0) & 0xFF

    # Save version and variant of UUID
    random_bytes[6] = (random_bytes[6] & 0x0F) | 0x70
    random_bytes[8] = (random_bytes[8] & 0x3F) | 0x80

    # Format UUID string
    uuid = random_bytes.hex()

    # Format separates UUID string
    return f"{uuid[0:8]}-{uuid[8:12]}-{uuid[12:16]}-{uuid[16:20]}-{uuid[20:32]}"


# ==-----------------------------------------------------------------------------== #
# Time functions                                                                    #
# ==-----------------------------------------------------------------------------== #
def seconds_duration(duration: str) -> int:
    """Get seconds duration according to given pattern."""

    # Time second units definitions
    time_units_in_seconds = {
        "s": 1,
        "m": 60,
        "h": 60 * 60,
        "d": 60 * 60 * 24,
        "w": 60 * 60 * 24 * 7,
        "M": 60 * 60 * 24 * 30,
        "y": 60 * 60 * 24 * 365
    }

    # Result seconds duration
    result_duration = 0

    # Convert time units to seconds duration
    for time_unit in re.findall(r"-?[\d]+[smhdwMy]", duration.replace(" ", str())):

        # Check sign of time unit
        match time_unit[0]:

            case "-":
                result_duration -= int(time_unit[1:-1]) * time_units_in_seconds[time_unit[-1]]

            case _:
                result_duration += int(time_unit[:-1]) * time_units_in_seconds[time_unit[-1]]

    return result_duration


functions_list = [item for item in dict(globals()).values() if hasattr(item, "__code__")]
