using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;

namespace UGCTools.Runtime
{
    [Flags]
    public enum MusicType
    {
        Happy = 1 << 0,
        Fierce = 1 << 1,
        Calm = 1 << 2,
        Groovy = 1 << 3,
        Romantic = 1 << 4,
        Funny = 1 << 5,
        Fitness = 1 << 6,
    }

    [System.Serializable]
    public class AudioContext
    {
        public string coverImageName = string.Empty;
        public string musicName = string.Empty;
        public string singerName = string.Empty;
        public int musicStarLevel = 0;
        public int musicBpm = 0;
        public float musicStartTime = 0;
        public float musicLength = 0;
        public int musicType = 0;
        public float startTime = 0;
        public float endTime = -1;
        public float speed = 1;
        public int exerciseIntensity = 0;
        public string hashCode = string.Empty;
        public string imageHashCode = string.Empty;
        public long musicId = 0;

        public string TosString()
        {
            return $"{{coverImageName:{coverImageName}, musicName:{musicName}, singerName:{singerName}, musicStarLevel:{musicStarLevel}, musicBpm:{musicBpm}, musicStartTime:{musicStartTime}, musicLength:{musicLength}, musicType:{musicType}, startTime:{startTime}, endTime:{endTime}, speed:{speed}, exerciseIntensity:{exerciseIntensity}, hashCode:{hashCode}, musicId:{musicId}}}";
        }
    }

    [System.Serializable]
    public class AnimationContext
    {
        public float startTime = 0;
        public float length = 0;
        public float endTime = -1;
        public float speed = 1;
        public string hashCode = string.Empty;
        public bool ikOnFoot = true;
        public string TosString()
        {
            return $"{{startTime:{startTime}, length:{length}, endTime:{endTime}, speed:{speed}, hashCode:{hashCode}}}";
        }
    }

    [System.Serializable]
    public class UGCContextConfig
    {
        public string animationName = string.Empty;
        public string audioName = string.Empty;
        public long timeStamp = 0;
        public AudioContext audioContext = null;
        public AnimationContext animationContext = null;

        public string TosString()
        {
            return $"{{animationName:{animationName}, audioName:{audioName}, audioContext:{audioContext.TosString()}, animationContext:{animationContext.TosString()}}}";
        }
    }
    public static class UGCUtils
    {
        private static string mUGCRoot = "Contexts";
        private static bool hasInit = false;
        private static string mUGCContextRoot = "Contexts";

        public static string UGCContextRoot
        {
            get
            {
                if (!hasInit)
                {
                    InitUGCRoot();
                }
                return mUGCContextRoot;
            }
        }

        public static void InitUGCRoot()
        {
            if (hasInit)
            {
                return;
            }
            string ugcRoot = GetUGCRoot();
            if (!Directory.Exists(ugcRoot))
            {
                Directory.CreateDirectory(ugcRoot);
            }
            hasInit = true;
            mUGCContextRoot = ugcRoot;
        }

        public static string GetUGCRoot()
        {
            return Path.Combine(Application.persistentDataPath, mUGCRoot);
        }

        public static void Log(string message)
        {
            Debug.Log(message);
        }

        // ѹContextĿ¼µzipļ
        public static void UnZipAllContexts()
        {
            string zipRoot = UGCContextRoot;
            if (!Directory.Exists(zipRoot))
            {
                Directory.CreateDirectory(zipRoot);
                return;
            }

            try
            {
                string[] zipFiles = Directory.GetFiles(zipRoot, "*.zip");
                foreach (string zipFile in zipFiles)
                {
                    string zipFileName = Path.GetFileNameWithoutExtension(zipFile);
                    string extractPath = Path.Combine(zipRoot, zipFileName);
                    if (Directory.Exists(extractPath))
                    {
                        Directory.Delete(extractPath, true);
                    }
                    Directory.CreateDirectory(extractPath);
                    ZipFile.ExtractToDirectory(zipFile, extractPath);
                    //Delete zip file
                    File.Delete(zipFile);
                }
            }
            catch (System.Exception e)
            {
                Debug.LogError(e.Message);
            }
        }

        public static Task UnZipAllContextsAsync(Action callbck = null)
        {
            return Task.Run(() =>
            {
                UnZipAllContexts();
                callbck?.Invoke();
            });
        }

        public static void GetAllUGCContextConfigs(ref string[] outContextNames, ref UGCContextConfig[] outContextConfigs)
        {
            string contextRoot = UGCContextRoot;
            if (!Directory.Exists(contextRoot))
            {
                //return null;
                Directory.CreateDirectory(contextRoot);
                outContextConfigs = null;
                outContextNames = null;
                return;
            }
            string[] contextDirs = Directory.GetDirectories(contextRoot);
            List<UGCContextConfig> contextConfigs = new List<UGCContextConfig>();
            List<string> contextNames = new List<string>();
            for (int i = 0; i < contextDirs.Length; i++)
            {
                // get json file
                string contextDir = contextDirs[i];
                string[] jsonFiles = Directory.GetFiles(contextDir, "*.json");
                if (jsonFiles.Length == 0)
                {
                    continue;
                }
                string jsonFile = jsonFiles[0];
                //ļɰutf-8ʽַ
                try
                {
                    string jsonContent = File.ReadAllText(jsonFile);
                    UGCContextConfig contextConfig = JsonUtility.FromJson<UGCContextConfig>(jsonContent);
                    contextConfigs.Add(contextConfig);
                    contextNames.Add(Path.GetFileName(contextDir));
                }
                catch (Exception ex) 
                {
                    Debug.LogError(ex.Message);
                }
            }
            outContextNames = contextNames.ToArray();
            outContextConfigs = contextConfigs.ToArray();
        }

        public static Task GetAllUGCContextConfigsAsync(Action<string[], UGCContextConfig[]> callback)
        {
            return Task.Run(() =>
            {
                string[] contextNames = null;
                UGCContextConfig[] contextConfigs = null;
                GetAllUGCContextConfigs(ref contextNames, ref contextConfigs);
                callback?.Invoke(contextNames, contextConfigs);
            });
        }

        public static void GetUGCContextConfigHash(string contextFile, string audioName, string animationName, out string audioHash, out string animationHash)
        {
            audioHash = string.Empty;
            animationHash = string.Empty;
            string contextRootPath = UGCContextRoot;
            if (!Directory.Exists(contextRootPath))
            {
                Debug.LogWarning($"Context root path not exist. {contextRootPath}");
                return;
            }
            string contextFolder = Path.Combine(contextRootPath, contextFile);
            if (!Directory.Exists(contextFolder))
            {
                Debug.LogWarning($"Context folder not exist. {contextFolder}");
                return;
            }
            string platform = "StandaloneWindows";
#if UNITY_ANDROID
            platform = "Android";
#endif
#if UNITY_WEBGL
            platform = "WebGL";
#endif
            string assetbundleManifestPath = Path.Combine(contextFolder, platform, platform);
            if (!File.Exists(assetbundleManifestPath))
            {
                Debug.LogWarning($"AssetBundle manifest not exist. {assetbundleManifestPath}");
                return;
            }

            AssetBundle manifestBundle = AssetBundle.LoadFromFile(assetbundleManifestPath);
            if (manifestBundle == null)
            {
                Debug.LogError("Failed to load AssetBundle manifest.");
                return;
            }

            AssetBundleManifest manifest = manifestBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
            if (manifest == null)
            {
                Debug.LogError("Failed to load AssetBundleManifest from AssetBundle.");
                manifestBundle.Unload(false);
                return;
            }

            Hash128 audioHash128 = manifest.GetAssetBundleHash(audioName);
            Hash128 animationHash128 = manifest.GetAssetBundleHash(animationName);

            audioHash = audioHash128.ToString();
            animationHash = animationHash128.ToString();

            manifestBundle.Unload(false);
        }

        public static T LoadAssetBundle<T>(string path) where T : UnityEngine.Object
        {
            return AssetBundlesManager.Instance.LoadAssetBundle<T>(path);
        }

        public static void LoadAssetBundleAsync<T>(string path, System.Action<T> callback) where T : UnityEngine.Object
        {
            AssetBundlesManager.Instance.LoadAssetBundleAsync<T>(path, callback);
        }

        public static void UnloadAssetBundle(string path)
        {
            AssetBundlesManager.Instance.UnloadAssetBundle(path);
        }
    }

    public static class SimpleEncryptor
    {
        private static readonly byte[] key = { 0x6D, 0x79, 0x5F, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x5F, 0x6B, 0x65, 0x79 }; // ԶԿ

        public static byte[] Encrypt(byte[] input)
        {
            byte[] encrypted = new byte[input.Length];
            for (int i = 0; i < input.Length; i++)
            {
                byte c = (byte)(input[i] ^ key[i % key.Length]);
                encrypted[i] = c;
            }
            return encrypted;
        }

        public static byte[] Decrypt(byte[] input)
        {
            return Encrypt(input); // ΪǶԳƼܣܷͼܷͬ
        }
    }
}