using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Threading.Tasks;
using UnityEditor;
using UnityEditor.PackageManager;
using UnityEditor.PackageManager.Requests;
using UnityEngine;
using UnityEngine.Networking;

public static class InstallLionSDK
{
    const string PACKAGE_NAME = "com.lionstudios.release.bundle";
    const string REGISTRY_NAME = "Lion Studios";
    const string REGISTRY_SCOPE = "com.lionstudios.release";
    const string REGISTRY_URL = "http://packages.lionstudios.cc:4873";
    const string QA_SHEET_URL = "https://docs.google.com/spreadsheets/d/e/2PACX-1vRwPfavl63ZqYVo-ChQ81em5zRtJMeSyE5c-7QWcN4qdnu_zCiQGAebk7_a2n22p_1WT6A7ELAfW8-f/pub?gid=0&single=true&output=csv";
    const string SDK_NAME_IN_SHEET = "LionSDK";
    const int HEADER_ROWS_TO_SKIP = 5;

    const string SESSION_STATE_STARTED = "LionInstaller/Started";
    const string SESSION_STATE_DONE = "LionInstaller/Done";
    const int CUSTOM_DELAY = 100;

    [InitializeOnLoadMethod]
    private static async void OnLoad()
    {
        if (SessionState.GetBool(SESSION_STATE_DONE, false)) return;

        SessionState.SetBool(SESSION_STATE_STARTED, true);
        UnityEditor.PackageManager.Events.registeredPackages += OnRegistered;

        try
        {
            if (await IsPackageInstalled())
            {
                Finish();
                return;
            }

            if (!ManifestHasRegistry(REGISTRY_URL))
            {
                await AddScopedRegistry(REGISTRY_URL);
            }

            string version = await GetStableVersion();

            // Fallback to latest from registry if spreadsheet fetch fails
            if (string.IsNullOrEmpty(version))
            {
                Debug.LogWarning("[INS]: Stable version not found from QA sheet, falling back to latest from registry.");
                version = await GetLatestVersion();
            }

            if (string.IsNullOrEmpty(version))
            {
                Debug.LogError("[INS]: Could not resolve version.");
                return;
            }

            Debug.Log($"[INS]: Installing version {version}");
            await InstallPackage(version);
        }
        catch (Exception e)
        {
            Debug.LogError($"[INS]: Exception {e}");
            SessionState.SetBool(SESSION_STATE_STARTED, false);
        }
    }

    private static async Task<bool> IsPackageInstalled()
    {
        var req = Client.List(true, true);
        while (!req.IsCompleted) await Task.Delay(CUSTOM_DELAY);
        return req.Result.Any(p => p.name == PACKAGE_NAME);
    }

    private static async Task AddScopedRegistry(string registryUrl)
    {
        BindingFlags flags = BindingFlags.Static | BindingFlags.NonPublic;
#if UNITY_6000_0_OR_NEWER
        Type[] sig = new[] { typeof(string), typeof(string), typeof(string[]), typeof(bool) };
        object[] args = new object[] { REGISTRY_NAME, registryUrl, new[] { REGISTRY_SCOPE }, false };
#else
        Type[] sig = new[] { typeof(string), typeof(string), typeof(string[]) };
        object[] args = new object[] { REGISTRY_NAME, registryUrl, new[] { REGISTRY_SCOPE } };
#endif
        MethodInfo method = typeof(Client).GetMethod("AddScopedRegistry", flags, null, sig, null);
        if (method == null)
        {
            Debug.LogWarning("[INS]: AddScopedRegistry not found via reflection.");
            return;
        }

        Request<RegistryInfo> req = (Request<RegistryInfo>)method.Invoke(null, args);
        while (!req.IsCompleted)
        {
            await Task.Delay(CUSTOM_DELAY);
        }
    }

    private static async Task<string> GetStableVersion()
    {
        try
        {
            string csvText = await DownloadCsvAsync(QA_SHEET_URL);
            if (string.IsNullOrEmpty(csvText))
            {
                return null;
            }

            return ParseStableVersionFromCsv(csvText, SDK_NAME_IN_SHEET);
        }
        catch (Exception e)
        {
            Debug.LogError($"[INS]: Error fetching stable version: {e.Message}");
            return null;
        }
    }

    private static async Task<string> GetLatestVersion()
    {
        SearchRequest req = Client.Search(PACKAGE_NAME);
        while (!req.IsCompleted)
        {
            await Task.Delay(CUSTOM_DELAY);
        }

        if (req.Status != StatusCode.Success || req.Result == null || req.Result.Length == 0)
        {
            return "";
        }

        string latestCompatible = req.Result[0]?.versions?.latestCompatible;
        return latestCompatible ?? "";
    }

    private static async Task<string> DownloadCsvAsync(string url)
    {
        using (UnityWebRequest www = UnityWebRequest.Get(url))
        {
            ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };

#pragma warning disable CS0618
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3
                | SecurityProtocolType.Tls12
                | SecurityProtocolType.Tls11
                | SecurityProtocolType.Tls;
#pragma warning restore CS0618

            www.SetRequestHeader("Content-Type", "text/plain");
            UnityWebRequestAsyncOperation operation = www.SendWebRequest();

            while (!operation.isDone)
            {
                await Task.Delay(CUSTOM_DELAY);
            }

#if UNITY_2020_3_OR_NEWER
            if (www.result != UnityWebRequest.Result.Success)
#else
            if (www.isNetworkError || www.isHttpError || !string.IsNullOrWhiteSpace(www.error))
#endif
            {
                Debug.LogError($"[INS]: Failed to download CSV: {www.error}");
                return null;
            }

            return www.downloadHandler.text;
        }
    }

    private static string ParseStableVersionFromCsv(string csvText, string targetName)
    {
        using (StringReader reader = new StringReader(csvText))
        {
            for (int i = 0; i < HEADER_ROWS_TO_SKIP; i++)
            {
                reader.ReadLine();
            }

            string line;
            while ((line = reader.ReadLine()) != null)
            {
                if (string.IsNullOrWhiteSpace(line))
                {
                    continue;
                }

                string[] values = line.Split(',');
                if (values.Length < 3)
                {
                    continue;
                }

                string codeName = values[1].Trim();
                if (string.IsNullOrEmpty(codeName))
                {
                    continue;
                }

                if (!string.Equals(codeName, targetName, StringComparison.OrdinalIgnoreCase))
                {
                    continue;
                }

                string version = values[2].Trim();
                if (!string.IsNullOrEmpty(version))
                {
                    return version;
                }
            }
        }

        return null;
    }

    private static async Task InstallPackage(string version)
    {
        var req = Client.Add($"{PACKAGE_NAME}@{version}");
        while (!req.IsCompleted) await Task.Delay(CUSTOM_DELAY);
        if (req.Status == StatusCode.Failure)
        {
            Debug.LogError($"[INS]: Add failed: {req.Error?.message}");
        }
    }

    private static bool ManifestHasRegistry(string url)
    {
        try
        {
            string manifest = File.ReadAllText(Path.Combine(Directory.GetCurrentDirectory(), "Packages/manifest.json"));
            return manifest.Contains(url);
        }
        catch { return false; }
    }

    private static void OnRegistered(PackageRegistrationEventArgs e)
    {
        bool added = e.added.Any(p => p.name == PACKAGE_NAME);
        if (added)
            Finish();
    }

    private static async void Finish()
    {
        UnityEditor.PackageManager.Events.registeredPackages -= OnRegistered;
        
        // Delete after compilation finishes
        while (EditorApplication.isCompiling || EditorApplication.isUpdating)
        {
            Debug.Log("[INS]: Waiting for compilation");
            await Task.Delay(CUSTOM_DELAY);
        }

        try
        {
            AssetDatabase.DeleteAsset("Assets/Editor/InstallLionSDK.cs");
            Debug.Log("[INS]: Installer deleted successfully");
            SessionState.SetBool(SESSION_STATE_DONE, true);
            SessionState.SetBool(SESSION_STATE_STARTED, false);
        }
        catch (Exception e)
        {
            Debug.LogWarning($"[INS]: Delete failed {e.Message}");
        }
    }
}
