import re
import os
from pathlib import Path
from fnmatch import fnmatch
from glob import glob
import string


class TemplateFormatter(string.Formatter):
    def format_field(self, value, format_spec):
        if isinstance(value, str):
            spec, sep = [*format_spec.split(':'), None][:2]
            
            if sep:
                value = value.replace('_', ' ')
                value = value = re.sub(r'([a-z])([A-Z])', rf'\1{sep}\2', value)
                value = value.replace(' ', sep)
            
            if spec == 'u':
                value = value.upper()
            elif spec == 'l':
                value = value.lower()
            elif spec == 't':
                value = value.title()

        return super().format(value, format_spec)

class Template:
    field_pattern = re.compile(r'{(\w+)\*{0,2}}')
    field_pattern_recursive = re.compile(r'{(\w+)\*{2}}')

    def __init__(self, template):
        #asset_data_path = Path(lib_path) / ASSETLIB_FILENAME

        self.raw = template
        self.formatter = TemplateFormatter()

    @property
    def glob_pattern(self):
        pattern = self.field_pattern_recursive.sub('**', self.raw)
        pattern = self.field_pattern.sub('*', pattern)
        return pattern

    @property
    def re_pattern(self):
        pattern = self.field_pattern_recursive.sub('([\\\w -_.\/]+)', self.raw)
        pattern = self.field_pattern.sub('([\\\w -_.]+)', pattern)
        pattern = pattern.replace('?', '.')
        pattern = pattern.replace('*', '.*')

        return re.compile(pattern)

    @property
    def fields(self):
        return self.field_pattern.findall(self.raw)
        #return [f or '0' for f in fields]

    def parse(self, path):

        path = Path(path).as_posix()

        res = self.re_pattern.findall(path)
        if not res:
            print('Could not parse {path} with {self.re_pattern}')
            return {}

        fields = self.fields

        if len(fields) == 1:
            field_values = res
        else:
            field_values = res[0]

        return {k:v for k,v in zip(fields, field_values)}

    def norm_data(self, data):
        norm_data = {}
        for k, v in data.items():

            if isinstance(v, Path):
                v = v.as_posix()

            norm_data[k] = v
        
        return norm_data

    def format(self, data=None, **kargs):

        data = {**(data or {}), **kargs}

        try:
            #print('FORMAT', self.raw, data)
            path = self.formatter.format(self.raw, **self.norm_data(data))
        except KeyError as e:
            print(f'Cannot format {self.raw} with {data}, field {e} is missing')
            return

        path = os.path.expandvars(path)
        return Path(path)

    def glob(self, directory, pattern=None):
        '''If pattern is given it need to be absolute'''
        if pattern is None:
            pattern = Path(directory, self.glob_pattern).as_posix()

        for entry in os.scandir(directory):
            entry_path = Path(entry.path)
            if entry.is_file() and fnmatch(entry_path.as_posix(), pattern):
                yield entry_path
            elif entry.is_dir():
                yield from self.glob(entry.path, pattern)

    def find(self, data, **kargs):
        pattern = self.format(data, **kargs)

        pattern_str = str(pattern)
        if '*' not in pattern_str and '?' not in pattern_str:
            return pattern

        paths = glob(pattern.as_posix())
        if paths:
            return Path(paths[0])
            
        #return pattern

    def __repr__(self):
        return f'Template({self.raw})'