import random
import re
import string

from httprider.core.faker_config import fake
from httprider.core.util_functions import *

# Internal function arguments must be one or more of
# \d (digit) \w (word) \" (") \* (*) \# (#) \- (-) \, (,) \s (space) \= \: (base64)
internal_func_rgx = re.compile(
    r"\$\{(\w+)\(([\d\w\"\*\#\-\,\.\/\=\s\:]*)\)\}", re.MULTILINE | re.IGNORECASE
)

address_attributes_map = {
    "country": fake.country,
    "address": fake.address,
    "secondary": fake.secondary_address,
    "street": fake.street_address,
    "city": fake.city,
    "zipcode": fake.zipcode,
    "state": fake.state,
}


def random_address(args):
    attribute = args
    return address_attributes_map.get(attribute.lower())()


person_attributes_map = {
    "male": {
        "prefix": fake.prefix_male,
        "first_name": fake.first_name_male,
        "last_name": fake.last_name_male,
        "full_name": fake.name_male,
        "suffix": fake.suffix_male,
    },
    "female": {
        "prefix": fake.prefix_female,
        "first_name": fake.first_name_female,
        "last_name": fake.last_name_female,
        "full_name": fake.name_female,
        "suffix": fake.suffix_female,
    },
}


def random_person(args):
    attribute, gender = args
    return person_attributes_map.get(gender.lower()).get(attribute)()


def custom_string_generator(args):
    pattern, upper_case = args

    def subs(chr):
        if chr == "*":
            if upper_case:
                return random.choice(string.ascii_uppercase)
            else:
                return random.choice(string.ascii_lowercase)
        if chr == "#":
            return random.choice(string.digits)
        return chr

    return "".join([subs(c) for c in pattern])


def random_uuid():
    return fake.uuid4()


def random_string_generator(args):
    chars, letters, digits, *specials = args
    specials = specials or False

    selection = ""
    if letters:
        selection += string.ascii_letters
    if digits:
        selection += string.digits
    if specials:
        selection += string.punctuation

    if not selection:
        selection = string.digits + string.ascii_letters + string.punctuation

    return "".join(random.choice(selection) for i in range(int(chars)))


def utils_func_applicator(args):
    func_name, params = args
    return utility_func_map.get(func_name)(params)


def noop(args):
    return f"Invalid function called with {args}"


def call_generator_func(func_name, parsed_args):
    args = None if not parsed_args else eval(parsed_args)
    m = {
        "random": random_string_generator,
        "uuid": random_uuid,
        "custom": custom_string_generator,
        "person": random_person,
        "address": random_address,
        "file": file_func_generator,
        "utils": utils_func_applicator,
    }

    generator_function = m.get(func_name)
    if generator_function and args:
        return generator_function(args)
    elif generator_function and not args:
        return generator_function()
    else:
        return noop(args)


# Accepts a regex match object which should have matched the regex for internal_func_rgx
# So the task for this method is to extract the function name and any arguments
# Then it calls gen_map to call the function (with args) and return the result back
# The result is then substituted in place of the function in the original string (templated_string)
# for eg. ${random(32, True, True)} will be substituted with the random value generated
def return_func_result(s):
    return call_generator_func(s.group(1), s.group(2))


def file_func_generator(args, wrap_in_quotes=False):
    if wrap_in_quotes:
        return f'${{file("{args}")}}'
    else:
        return f"${{file({args})}}"


def is_file_function(func_value):
    return internal_func_rgx.search(func_value)
