# SPDX-License-Identifier: GPL-2.0-or-later


"""
Function relative to the asset browser addon
"""

from pathlib import Path
import json
import os
import re

import time
#from asset_library.constants import ASSETLIB_FILENAME
import inspect
from asset_library.common.file_utils import read_file
from asset_library.common.bl_utils import get_addon_prefs
import uuid

import bpy


def command(func):
    '''Decorator to be used from printed functions argument and run time'''
    func_name = func.__name__.replace('_', ' ').title()
    
    def _command(*args, **kargs):

        bound = inspect.signature(func).bind(*args, **kargs)
        bound.apply_defaults()

        args_str = ', '.join([f'{k}={v}' for k, v in bound.arguments.items()])
        print(f'\n[>-] {func_name} ({args_str}) --- Start ---')

        t0 = time.time()
        result = func(*args, **kargs)

        print(f'[>-] {func_name} --- Finished (total time : {time.time() - t0:.2f}s) ---')
        return result
    
    return _command  

def asset_warning_callback(self, context):
    """Callback function to display a warning message when ading or modifying an asset"""
    self.warning = ''

    if not self.name:
        self.warning = 'You need to specify a name'
        return
    if not self.catalog:
        self.warning = 'You need to specify a catalog'
        return

    lib = get_active_library()
    action_path = lib.adapter.get_asset_relative_path(self.name, self.catalog)
    self.path = action_path.as_posix()

    if lib.merge_libraries:
        prefs = get_addon_prefs()
        lib = prefs.libraries[lib.store_library]

    if not lib.adapter.get_asset_path(self.name, self.catalog).parents[1].exists():
        self.warning = 'A new folder will be created'

def get_active_library():
    '''Get the pref library properties from the active library of the asset browser'''
    prefs = get_addon_prefs()
    asset_lib_ref = bpy.context.space_data.params.asset_library_ref

    #Check for merged library
    for l in prefs.libraries:
        if l.library_name == asset_lib_ref:
            return l

def get_active_catalog():
    '''Get the active catalog path'''

    lib = get_active_library()
    cat_data = lib.adapter.read_catalog()
    cat_data = {v['id']:k for k,v in cat_data.items()}

    cat_id = bpy.context.space_data.params.catalog_id
    if cat_id in cat_data:
        return cat_data[cat_id]

    return ''

"""
def norm_asset_datas(asset_file_datas):
    ''' Return a new flat list of asset data
        the filepath keys are merge with the assets keys'''
    
    asset_datas = []
    for asset_file_data in asset_file_datas:
        asset_file_data = asset_file_data.copy()
        if 'assets' in asset_file_data:

            assets = asset_file_data.pop('assets')
            for asset_data in assets:

                asset_datas.append({**asset_file_data, **asset_data})

        else:
            asset_datas.append(asset_file_data)
    
    return asset_datas

def cache_diff(cache, new_cache):
    '''Compare and return the difference between two asset datas list'''

    #TODO use an id to be able to tell modified asset if renamed
    #cache = {a.get('id', a['name']) : a for a in norm_asset_datas(cache)}
    #new_cache = {a.get('id', a['name']) : a for a in norm_asset_datas(new_cache)}

    cache = {f"{a['filepath']}/{a['name']}": a for a in norm_asset_datas(cache)}
    new_cache = {f"{a['filepath']}/{a['name']}" : a for a in norm_asset_datas(new_cache)}

    assets_added = [v for k, v  in new_cache.items() if k not in cache]
    assets_removed = [v for k, v  in cache.items() if k not in new_cache]
    assets_modified = [v for k, v in cache.items() if v not in assets_removed and v!= new_cache[k]]

    if assets_added:
        print(f'{len(assets_added)} Assets Added \n{tuple(a["name"] for a in assets_added[:10])}\n')
    if assets_removed:
        print(f'{len(assets_removed)} Assets Removed \n{tuple(a["name"] for a in assets_removed[:10])}\n')
    if assets_modified:
        print(f'{len(assets_modified)} Assets Modified \n{tuple(a["name"] for a in assets_modified[:10])}\n')
    
    assets_added = [dict(a, operation='ADD') for a  in assets_added]
    assets_removed = [dict(a, operation='REMOVE') for a  in assets_removed]
    assets_modified = [dict(a, operation='MODIFY') for a in assets_modified]

    assets_diff = assets_added + assets_removed + assets_modified
    if not assets_diff:
        print('No change in the library')

    return assets_diff

def clean_default_lib():    
    prefs = bpy.context.preferences
    
    if not prefs.filepaths.asset_libraries:
        print('[>-] No Asset Libraries Filepaths Setted.')
        return

    lib, lib_id = get_lib_id(
        library_name='User Library',
        asset_libraries=prefs.filepaths.asset_libraries
    )
    if lib:
        bpy.ops.preferences.asset_library_remove(index=lib_id)

def get_asset_source(replace_local=False):
    sp = bpy.context.space_data
    prefs = bpy.context.preferences.addons[__package__].preferences
    asset_file_handle = bpy.context.asset_file_handle

    if asset_file_handle is None:
        return None

    if asset_file_handle.local_id:
        publish_path = os.path.expandvars(scn.actionlib.get('publish_path'))
        if not publish_path:
            print('[>.] No \'Publish Dir\' found. Publish file first.' )
            return None

        return Path(publish_path)

    asset_library_ref = bpy.context.asset_library_ref
    source_path = bpy.types.AssetHandle.get_full_library_path(asset_file_handle, asset_library_ref)

    if replace_local:
        if 'custom' in sp.params.asset_library_ref.lower():
            actionlib_path = prefs.action.custom_path
            actionlib_path_local = prefs.action.custom_path_local
        else:
            actionlib_path = prefs.action.path
            actionlib_path_local = prefs.action.path_local

        source_path = re.sub(actionlib_dir_local, actionlib_dir, source_path)

    return source_path
"""
'''
def get_catalog_path(filepath=None):
    filepath = filepath or bpy.data.filepath
    filepath = Path(filepath)

    if filepath.is_file():
        filepath = filepath.parent

    filepath.mkdir(parents=True, exist_ok=True)

    catalog = filepath / 'blender_assets.cats.txt'
    if not catalog.exists():
        catalog.touch(exist_ok=False)

    return catalog
'''

# def read_catalog(path, key='path'):
#     cat_data = {}

#     supported_keys = ('path', 'id', 'name')

#     if key not in supported_keys:
#         raise Exception(f'Not supported key: {key} for read catalog, supported keys are {supported_keys}')

#     for line in Path(path).read_text(encoding="utf-8").split('\n'):
#         if line.startswith(('VERSION', '#')) or not line:
#             continue

#         cat_id, cat_path, cat_name = line.split(':')

#         if key == 'id':
#             cat_data[cat_id] = {'path':cat_path, 'name':cat_name}
#         elif key == 'path':
#             cat_data[cat_path] = {'id':cat_id, 'name':cat_name}
#         elif key =='name':
#             cat_data[cat_name] = {'id':cat_id, 'path':cat_path}
    
#     return cat_data
"""
def read_catalog(path):
    cat_data = {}

    for line in Path(path).read_text(encoding="utf-8").split('\n'):
        if line.startswith(('VERSION', '#')) or not line:
            continue

        cat_id, cat_path, cat_name = line.split(':')
        cat_data[cat_path] = {'id':cat_id, 'name':cat_name}
    
    return cat_data

def write_catalog(path, data):
    lines = ['VERSION 1', '']

    # Add missing parents catalog
    norm_data = {}
    for cat_path, cat_data in data.items():
        norm_data[cat_path] = cat_data
        for p in Path(cat_path).parents[:-1]:
            if p in data or p in norm_data:
                continue
            
            norm_data[p.as_posix()] = {'id': str(uuid.uuid4()), 'name': '-'.join(p.parts)}

    for cat_path, cat_data in sorted(norm_data.items()):
        cat_name = cat_data['name'].replace('/', '-')
        lines.append(f"{cat_data['id']}:{cat_path}:{cat_name}")
    
    print(f'Catalog writen at: {path}')
    Path(path).write_text('\n'.join(lines), encoding="utf-8")

def create_catalog_file(json_path : str|Path, keep_existing_category : bool = True):
    '''create asset catalog file from json
    if catalog already exists, keep existing catalog uid'''

    json_path = Path(json_path)
    # if not json.exists(): return
    assert json_path.exists(), 'Json not exists !'
    
    category_datas = json.loads(json_path.read_text(encoding="utf-8"))
    
    catalog_path = json_path.parent / 'blender_assets.cats.txt'
    catalog_data = {}
    if catalog_path.exists():
        catalog_data = read_catalog(catalog_path)
        ## retrun a format catalog_data[path] = {'id':id, 'name':name}
        ## note: 'path' in catalog is 'name' in category_datas
    
    catalog_lines = ['VERSION 1', '']

    ## keep existing
    for c in category_datas:
        # keep same catalog line for existing category keys
        if keep_existing_category and catalog_data.get(c['name']):
            print(c['name'], 'category exists')
            cat = catalog_data[c['name']] #get 
            catalog_lines.append(f"{cat['id']}:{c['name']}:{cat['name']}")
        else:
            print(c['name'], 'new category')
            # add new category 
            catalog_lines.append(f"{c['id']}:{c['name']}:{c['name'].replace('/', '-')}")
    
    ## keep category that are non-existing in json ?
    if keep_existing_category:
        for k in catalog_data.keys():
            if next((c['name'] for c in category_datas if c['name'] == k), None):
                continue
            print(k, 'category not existing in json')
            cat = catalog_data[k]
            # rebuild existing line
            catalog_lines.append(f"{cat['id']}:{k}:{cat['name']}")

    ## write_text overwrite the file
    catalog_path.write_text('\n'.join(catalog_lines), encoding="utf-8")

    print(f'Catalog saved at: {catalog_path}')

    return 
"""

def clear_env_libraries():
    print('clear_env_libraries')

    prefs = get_addon_prefs()
    asset_libraries = bpy.context.preferences.filepaths.asset_libraries

    for env_lib in prefs.env_libraries:
        name = env_lib.get('asset_library')
        if not name:
            continue
        
        asset_lib = asset_libraries.get(name)
        if not asset_lib:
            continue

        index = list(asset_libraries).index(asset_lib)
        bpy.ops.preferences.asset_library_remove(index=index)
    
    prefs.env_libraries.clear()

'''
    env_libs = get_env_libraries()
    paths = [Path(l['path']).resolve().as_posix() for n, l in env_libs.items()]
    
    for i, l in reversed(enumerate(libs)):
        lib_path = Path(l.path).resolve().as_posix()

        if (l.name in env_libs or lib_path in paths):
            libs.remove(i)
            '''
            
def set_env_libraries(path=None) -> list:
    '''Read the environments variables and create the libraries'''

    #from asset_library.prefs import AssetLibraryOptions
    prefs = get_addon_prefs()
    path = path or prefs.config_directory

    #print('Read', path)
    library_data = read_file(path)

    clear_env_libraries()

    if not library_data:
        return

    libs = []

    for lib_info in library_data:
        lib = prefs.env_libraries.add()

        lib.set_dict(lib_info)

        libs.append(lib)

    return libs

'''
def get_env_libraries():
    env_libraries = {}

    for k, v in os.environ.items():
        if not re.findall('ASSET_LIBRARY_[0-9]', k):
            continue

        lib_infos = v.split(os.pathsep)

        if len(lib_infos) == 5:        
            name, data_type, tpl, src_path, bdl_path = lib_infos
        elif len(lib_infos) == 4:
            name, data_type, tpl, src_path = lib_infos
            bdl_path = ''
        else:
            print(f'Wrong env key {k}', lib_infos)
            continue
        
        source_type = 'TEMPLATE'
        if tpl.lower().endswith(('.json', '.yml', 'yaml')):
            source_type = 'DATA_FILE'

        env_libraries[name] = {
            'data_type': data_type, 
            'source_directory': src_path, 
            'bundle_directory': bdl_path,
            'source_type': source_type,
            'template': tpl,
            }
    
    return env_libraries
'''





def resync_lib(name, waiting_time):
    bpy.app.timers.register(
        lambda: bpy.ops.assetlib.synchronize(only_recent=True, name=name),
        first_interval=waiting_time
    )



'''
def set_assetlib_paths():
    prefs = bpy.context.preferences

    assetlib_name = 'Assets'
    assetlib = prefs.filepaths.asset_libraries.get(assetlib_name)

    if not assetlib:
        bpy.ops.preferences.asset_library_add(directory=str(assetlib_path))
        assetlib = prefs.filepaths.asset_libraries[-1]
        assetlib.name = assetlib_name
    
    assetlib.path = str(actionlib_dir)

def set_actionlib_paths():
    prefs = bpy.context.preferences

    actionlib_name = 'Action Library'
    actionlib_custom_name = 'Action Library Custom'

    actionlib = prefs.filepaths.asset_libraries.get(actionlib_name)

    if not assetlib:
        bpy.ops.preferences.asset_library_add(directory=str(assetlib_path))
        assetlib = prefs.filepaths.asset_libraries[-1]
        assetlib.name = assetlib_name

    actionlib_dir = get_actionlib_dir(custom=custom)
    local_actionlib_dir = get_actionlib_dir(local=True, custom=custom)

    if local_actionlib_dir:
        actionlib_dir = local_actionlib_dir

    if actionlib_name not in prefs.filepaths.asset_libraries:
        bpy.ops.preferences.asset_library_add(directory=str(actionlib_dir))

    #lib, lib_id = get_lib_id(
    #    library_path=actionlib_dir,
    #    asset_libraries=prefs.filepaths.asset_libraries
    #)

    #if not lib:
    #    print(f'Cannot set dir for {actionlib_name}')
    #    return

    prefs.filepaths.asset_libraries[lib_id].name = actionlib_name
    #prefs.filepaths.asset_libraries[lib_id].path = str(actionlib_dir)
    '''












