from functools import lru_cache
from typing import Sequence
import string

from faker import Faker
from jinja2 import Template


class StringGenerator:
    """Sometimes in templates you want a reference to a variable to
    call a function.

    For example:

    >>> x = template_utils.StringGenerator(datetime.today().isoformat)
    >>> print(f"{x}")
    2019-09-23T11:49:01.994453

    >>> x = template_utils.StringGenerator(lambda:str(random.random()))
    >>> print(f"{x}")
    0.795273959965055
    >>> print(f"{x}")
    0.053061903749985206
    """

    def __init__(self, func):
        self.func = func

    def __str__(self):
        return str(self.func())

    def __call__(self, *args, **kwargs):
        return self.func(*args, **kwargs)


class FakerTemplateLibrary:
    """A Jinja template library to add the faker.xyz objects to templates"""

    def __init__(self, faker_providers: Sequence[object], locale=None):
        self.locale = locale
        self.faker = Faker(self.locale)
        for provider in faker_providers:
            self.faker.add_provider(provider)

    def __getattr__(self, name):
        return StringGenerator(
            lambda *args, **kwargs: self.faker.format(name, *args, **kwargs)
        )


Template = lru_cache(512)(Template)


number_chars = set(string.digits + ".")


def look_for_number(arg):
    looks_like_float = False
    if len(arg) == 0:
        return arg
    for char in arg:
        if char not in number_chars:
            return arg
        if char == ".":
            looks_like_float = True
    if looks_like_float:
        return float(arg)
    else:
        return int(arg)
