/*
<copyright file="BGExcelImageNewAssetsContainer.cs" company="BansheeGz">
    Copyright (c) 2018-2024 All Rights Reserved
</copyright>
*/

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using NPOI.XSSF.UserModel;
using UnityEditor;
using UnityEngine;
using File = UnityEngine.Windows.File;

namespace BansheeGz.BGDatabase.Editor
{
    public class BGExcelImageNewAssetsContainer
    {
        private readonly List<NewFieldAssets> fieldAssets = new List<NewFieldAssets>();


        private void Add(NewFieldAssets newFieldAssets) => fieldAssets.Add(newFieldAssets);

        public void Complete()
        {
            if (fieldAssets.Count == 0) return;

            var projectAssets = new FilesCollection(new DirectoryInfo(Application.dataPath), true);
            var addressablesUpdateCache = false;
            var updatableCaches = new List<BGAssetLoaderManagerA.UpdatableCache>();
            foreach (var fieldAsset in fieldAssets)
            {
                var imageField = fieldAsset.ImageField;
                var newAssetsFolder = fieldAsset.AssetsConfig.GetNewAssetFolder(imageField.field.Id);
                var newImages = fieldAsset.NewImages;
                foreach (var newImage in newImages)
                {
                    var basePath = GetFileBase(newImage.entity);
                    var uniqueName = basePath;
                    var counter = -1;
                    while (!projectAssets.IsAvailable(uniqueName))
                    {
                        counter++;
                        uniqueName = basePath + '_' + counter;
                    }

                    projectAssets.Add(uniqueName);

                    string fullPath = null;
                    try
                    {
                        //create Unity asset
                        var fullPathNoExt = Path.Combine(newAssetsFolder, uniqueName);
                        fullPath = Path.ChangeExtension(fullPathNoExt, newImage.fileExtension);
                        File.WriteAllBytes(fullPath, newImage.xssfPicture.PictureData.Data);
                        AssetDatabase.ImportAsset(fullPath);
                        newImage.Asset = AssetDatabase.LoadAssetAtPath<Texture>(fullPath);
                        if (newImage.Asset != null && imageField.IsSprite)
                        {
                            var importer = AssetImporter.GetAtPath(fullPath) as TextureImporter;
                            if (importer != null)
                            {
                                importer.textureType = TextureImporterType.Sprite;
                                importer.spriteImportMode = SpriteImportMode.Single;
                                AssetDatabase.ImportAsset(fullPath);
                            }
                        }
                    }
                    catch (Exception)
                    {
                        Debug.LogError(
                            $"Error while creating Unity asset, path={fullPath}, field={imageField.field.FullName}, " +
                            $"entity #={newImage.entity.Index}, entity name={BGEditorUtility.GetEntityName(newImage.entity)}");
                        throw;
                    }
                }

                if (imageField.loader is BGAssetLoaderAddressables) addressablesUpdateCache = true;
                if (imageField.loaderManager is BGAssetLoaderManagerA.UpdatableCache updatable
                    && updatableCaches.All(u => u.GetType() != updatable.GetType())) updatableCaches.Add(updatable);
            }

            if (addressablesUpdateCache)
            {
                try
                {
                    BGAssetLoaderManagerAddressables.UpdateCache();
                }
                catch (Exception e)
                {
                    Debug.LogException(e);
                }
            }

            foreach (var updatableCache in updatableCaches)
            {
                try
                {
                    updatableCache.Update();
                }
                catch (Exception e)
                {
                    Debug.LogException(e);
                }
            }

            foreach (var fieldAsset in fieldAssets)
            {
                var imageField = fieldAsset.ImageField;
                var newImages = fieldAsset.NewImages;
                foreach (var newImage in newImages)
                {
                    if (newImage.Asset == null) continue;

                    var loaderPath = imageField.loaderManager.ResolvePath(imageField.loader, newImage.Asset);
                    if (!string.IsNullOrEmpty(loaderPath)) imageField.SetStoredValue(newImage.entity.Index, loaderPath);
                }
            }
        }

        private static string GetFileBase(BGEntity entity)
        {
            var name = BGEditorUtility.GetEntityName(entity);
            if (string.IsNullOrEmpty(name)) return entity.MetaName;
            var builder = new StringBuilder();
            for (var i = 0; i < name.Length; i++)
            {
                var aChar = name[i];
                if (!char.IsLetter(aChar) && !char.IsDigit(aChar) && aChar != '_') continue;
                builder.Append(aChar);
            }

            if (builder.Length == 0) return entity.MetaName;
            return builder.ToString();
        }

        private class FilesCollection
        {
            private readonly HashSet<string> files = new HashSet<string>();

            public FilesCollection(DirectoryInfo folder, bool recursive) => GatherFiles(folder, recursive);

            private void GatherFiles(DirectoryInfo folder, bool recursive)
            {
                var folderFiles = folder.GetFiles();
                foreach (var fileInfo in folderFiles) files.Add(Path.GetFileNameWithoutExtension(fileInfo.Name));
                if (!recursive) return;

                var dirs = folder.GetDirectories();
                foreach (var dir in dirs) GatherFiles(dir, true);
            }

            public bool IsAvailable(string path) => !files.Contains(path);

            public void Add(string path) => files.Add(path);
        }

        public class ExcelImage
        {
            public readonly BGEntity entity;
            public readonly XSSFPicture xssfPicture;
            public readonly string fileExtension;

            public Texture Asset;

            public ExcelImage(BGEntity entity, XSSFPicture xssfPicture)
            {
                this.entity = entity;
                this.xssfPicture = xssfPicture;
                fileExtension = BGExcelImageAsset.GetExtension(xssfPicture.PictureData.PictureType);
            }
        }

        public class NewFieldAssets
        {
            private readonly List<ExcelImage> newImages = new List<ExcelImage>();
            private readonly BGExcelImageField imageField;
            private readonly BGSyncAssetsConfig assetsConfig;

            public List<ExcelImage> NewImages => newImages;

            public BGExcelImageField ImageField => imageField;

            public BGSyncAssetsConfig AssetsConfig => assetsConfig;

            public NewFieldAssets(BGExcelImageNewAssetsContainer container, BGExcelImageField imageField, BGSyncAssetsConfig assetsConfig)
            {
                this.imageField = imageField;
                this.assetsConfig = assetsConfig;
                container.Add(this);
            }

            public void Add(ExcelImage asset) => newImages.Add(asset);
        }
    }
}