"""
Util function for this addon
"""

import os
from pathlib import Path
import inspect
from datetime import datetime
from time import perf_counter
import platform

import bpy
from .catalog import Catalog, read_catalog
from .bl_utils import get_addon_prefs, get_asset_type
from .file_utils import read_file, write_file, cache, import_module_from_path


def thumbnail_blend_file(input_blend, output_img):
    input_blend = Path(input_blend).resolve()
    output_img = Path(output_img).resolve()

    print(f'Thumbnailing {input_blend} to {output_img}')
    blender_thumbnailer = Path(bpy.app.binary_path).parent / 'blender-thumbnailer'

    output_img.parent.mkdir(exist_ok=True, parents=True)

    subprocess.call([blender_thumbnailer, str(input_blend), str(output_img)])

    success = output_img.exists()

    if not success:
        empty_preview = RESOURCES_DIR / 'empty_preview.png'
        shutil.copy(str(empty_preview), str(output_img))

    return success


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

    #Check for merged library
    for l in prefs.libraries:
        if l.name == lib_ref:
            return l


def update_library_path():
    """Removing all asset libraries and recreate them"""

    print("update_library_path")

    addon_prefs = get_addon_prefs()
    libs = bpy.context.preferences.filepaths.asset_libraries

    for i, lib in reversed(list(enumerate(libs))):
        if (addon_lib := addon_prefs.libraries.get(lib.name)): # and addon_lib.path == lib.path
            bpy.ops.preferences.asset_library_remove(index=i)

    for addon_lib in addon_prefs.libraries:
        if not addon_lib.use:
            continue
        bpy.ops.preferences.asset_library_add(directory=str(addon_lib.path))
        libs[-1].name = addon_lib.name


def load_library_config(config_path):
    """"Load library prefs from config path"""
    if not config_path:
        return []

    config_path = Path(config_path)

    if not config_path.exists():
        print(f'Config {config_path} not exist')
        return []
    
    prefs = get_addon_prefs()

    libs = []
    for lib_dict in read_file(config_path):
        lib = prefs.libraries.add()
        lib.is_user = False
        lib.set_dict(lib_dict)
        libs.append(lib)

    return libs


def load_libraries():
    """"Load library prefs from config pref and env"""
    prefs = get_addon_prefs()

    bl_libs = bpy.context.preferences.filepaths.asset_libraries
    for i, bl_lib in reversed(list(enumerate(bl_libs))):
        addon_lib = prefs.libraries.get(bl_lib.name)
        if not addon_lib or addon_lib.is_user:
            continue

        bpy.ops.preferences.asset_library_remove(index=i)
    
    # Remove lib from addons preferences
    for i, addon_lib in reversed(list(enumerate(prefs.libraries))):
        if not addon_lib.is_user:
            prefs.libraries.remove(i)

    env_config = os.getenv('ASSET_LIBRARY_CONFIG')
    libs = load_library_config(env_config) + load_library_config(prefs.config_path)

    return libs


def clear_time_tag(asset):
    # Created time tag
    for tag in list(asset.asset_data.tags):
        try:
            datetime.strptime(tag.name, "%Y-%m-%d %H:%M")
            asset.asset_data.tags.remove(tag)
        except ValueError:
            continue


def create_time_tag(asset):
    asset.asset_data.tags.new(datetime.now().strftime("%Y-%m-%d %H:%M"))


def version_file(path, save_versions=3):
    if path.exists():
        for i in range(save_versions):
            version = save_versions - i
            version_path = path.with_suffix(f'.blend{version}')
            if not version_path.exists():
                continue
            if i == 0:
                version_path.unlink()
            else:
                version_path.rename(path.with_suffix(f'.blend{version+1}'))

        path.rename(path.with_suffix(f'.blend1'))


def list_datablocks(blend_file, asset_types={"objects", "materials", "node_groups"}):
    blend_data = {}
    with bpy.data.temp_data(filepath=str(blend_file)) as temp_data:
        with temp_data.libraries.load(str(blend_file), link=True) as (data_from, data_to):
            for asset_type in asset_types:
                blend_data[asset_type] = getattr(data_from, asset_type)

    return blend_data


def get_asset_data(blend_file, asset_type, name, preview=False):
    with bpy.data.temp_data(filepath=str(blend_file)) as temp_data:
        with temp_data.libraries.load(str(blend_file), link=True) as (data_from, data_to):
            if name not in getattr(data_from, asset_type):
                return
            setattr(data_to, asset_type, [name])
                
        if assets := getattr(data_to, asset_type):
            asset = assets[0]
            asset_data = asset.asset_data

            data = {
                "description": asset_data.description,
                "catalog_id": asset_data.catalog_id,
                "catalog_simple_name": asset_data.catalog_simple_name,
                "path": blend_file,
                "tags": list(asset_data.tags.keys())
            }

            if asset.preview and preview:
                image_size = asset.preview.image_size
                preview_pixels = [0] * image_size[0] * image_size[1] * 4
                asset.preview.image_pixels_float.foreach_get(preview_pixels)

                data["preview_pixels"] = preview_pixels
                data["preview_size"] = list(image_size)
            
            return data


def find_asset_data(name, asset_type, preview=False):
    """Find info about an asset found in library"""

    bl_libs = bpy.context.preferences.filepaths.asset_libraries

    # First search for a blend with the same name
    for bl_lib in bl_libs:
        for blend_file in Path(bl_lib.path).glob(f"**/{name}.blend"):
            if asset_data := get_asset_data(blend_file, asset_type, name, preview=preview):
                asset_data['library'] = bl_lib
                return asset_data

    # for bl_lib in bl_libs:
    #     for blend_file in Path(bl_lib.path).glob("**/*.blend"):
    #         if asset_data := get_asset_data(blend_file, asset_type, name):
    #             return bl_lib, asset_data


def get_filepath_library(filepath):
    for lib in bpy.context.preferences.filepaths.asset_libraries:
        if bpy.path.is_subdir(filepath, lib.path):
            return lib


def get_asset_catalog_path(asset, fallback=''):
    if asset.local_id:
        path = Path(bpy.data.filepath).parent

    elif (lib := get_filepath_library(asset.full_library_path)):
        if lib:
            path = lib.path
        else:
            return fallback

    if not (catalog := read_catalog(path)):
        return fallback
    
    if not (catalog_item := catalog.get(id=asset.metadata.catalog_id, fallback=fallback)):
        return fallback
    
    return catalog_item.path
     

def get_asset_full_path(asset):
    """Get a path that represent all informations about an asset path/type/catalog/name"""
    
    asset_path = asset.full_library_path
    if asset.local_id:
        asset_path = f'{bpy.data.filepath}/{asset_path}'

    asset_type, asset_name = Path(asset.full_path).parts[-2:]
    asset_type = get_asset_type(asset_type)

    return Path(asset_path, asset_type, asset_name).as_posix()


def get_asset_source(datablock):
    weak_reference = datablock.library_weak_reference
    if isinstance(datablock, bpy.types.Object) and datablock.data:
        weak_reference = datablock.data.library_weak_reference

    if weak_reference and (source_path := Path(weak_reference.filepath)).exists():
        return source_path

    asset_libraries = context.preferences.filepaths.asset_libraries
    for asset_library in asset_libraries:
        library_path = Path(asset_library.path)
        if blend_files := list(library_path.glob(f"**/{datablock.name}.blend")):
            return

    return datablock.library_weak_reference


def get_blender_cache_dir():
    if platform.system() == 'Linux':
        cache_folder = os.path.expandvars('$HOME/.cache/blender')
    elif platform.system() == 'Windows':
        cache_folder = os.path.expanduser('%USERPROFILE%/AppData/Local/Blender Foundation/Blender')
    elif platform.system() == 'Darwin':
        cache_folder = '/Library/Caches/Blender'

    return Path(cache_folder)


def find_asset_source(library_map, asset_type, name):
    return next( (l for l, blend_data in sorted(library_map.items(), key=lambda x: x[1]['st_mtime'], reverse=True) 
                 if name in blend_data['node_groups']), None)


def asset_library_map():
    """"Get a mapping of all datablocks of the blend files from the libraries"""
    asset_libraries = bpy.context.preferences.filepaths.asset_libraries
    
    cache_file = get_blender_cache_dir() / 'asset-library.json'
    cache = None
    if cache_file.exists():
        cache = read_file(cache_file)

    if cache is None:
        cache = {}
    
    file_keys = []

    for asset_library in asset_libraries:
        library_path = Path(asset_library.path)

        for bl_file in library_path.glob("**/*.blend"):
            file_keys.append(file_key := bl_file.as_posix())

            st_mtime = bl_file.stat().st_mtime
            if (bl_cache := cache.get(file_key)) and bl_cache.get('st_mtime') >= st_mtime:
                continue

            datablocks = list_datablocks(bl_file)
            cache[file_key] = dict(st_mtime=st_mtime, **datablocks)
    
    # Remove map when the blend not exists anymore
    for file_key in list(cache.keys()):
        if file_key not in file_keys:
            del cache[file_key]

    write_file(cache_file, cache)

    return cache
    
#     print(perf_counter() - t0)

    # t0 = perf_counter()
    # for asset_library in asset_libraries:
    #     library_path = Path(asset_library.path)

    #     for blend_file in library_path.glob("**/*.blend"):
    #         with bpy.data.libraries.load(str(blend_file), link=True) as (data_from, data_to):
    #             node_groups = data_from.node_groups
    #         print(node_groups)
    
    # print(perf_counter() - t0)