using System.Diagnostics;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using SingularityGroup.HotReload.Newtonsoft.Json;
using UnityEngine;
using System;
using SingularityGroup.HotReload.DTO;
using SingularityGroup.HotReload.Localization;

namespace SingularityGroup.HotReload.Editor.Cli {
    internal static class CliUtils {
        static readonly string projectIdentifier = GetProjectIdentifier();

        class Config {
            public bool singleInstance;
        }

        public static string GetProjectIdentifier() {
            if (File.Exists(PackageConst.ConfigFilePath)) {
                var config = JsonConvert.DeserializeObject<Config>(File.ReadAllText(PackageConst.ConfigFilePath));
                if (config.singleInstance) {
                    return null;
                }
            }
            var path = Path.GetFullPath(MultiplayerPlaymodeHelper.PathToMainProject("."));
            var name = new DirectoryInfo(path).Name;
            using (SHA256 sha256 = SHA256.Create()) {
                byte[] inputBytes = Encoding.UTF8.GetBytes(path);
                byte[] hashBytes = sha256.ComputeHash(inputBytes);
                var hash = BitConverter.ToString(hashBytes).Replace("-", "").Substring(0, 6).ToUpper();
                return $"{name}-{hash}";
            }
        }
        
        public static string GetTempDownloadFilePath(string osxFileName) {
            if (UnityHelper.Platform == RuntimePlatform.OSXEditor) {
                // project specific temp directory that is writeable on MacOS (Path.GetTempPath() wasn't when run through HotReload.app)
                return Path.GetFullPath(PackageConst.LibraryCachePath + $"/HotReloadServerTemp/{osxFileName}");
            } else {
                return Path.GetTempFileName();
            }
        }
        
        public static string GetHotReloadTempDir() {
            if (UnityHelper.Platform == RuntimePlatform.WindowsEditor) {
                // library path interfereces with VS Code file watcher (Source Control window doesn't auto refresh)
                // so we pick data path on windows instead
                if (projectIdentifier != null) {
                    return Path.Combine(GetAppDataPath(), "HotReloadServerTemp", projectIdentifier);
                } else {
                    return Path.Combine(GetAppDataPath(), "HotReloadServerTemp");
                }
            }
            // store in library path on mac and linux since it works there
            return Path.GetFullPath(Path.Combine(PackageConst.LibraryCachePath, "HotReloadServerTemp"));
        }
        
        public static string GetAppDataPath() {
#           if (UNITY_EDITOR_OSX)
                var baseDir = "/Users/Shared";
#           elif (UNITY_EDITOR_LINUX)
                var baseDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
#           else
                var baseDir = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
#endif
            return Path.Combine(baseDir, "singularitygroup-hotreload");
        }
        
        public static string GetExecutableTargetDir() {
            if (PackageConst.IsAssetStoreBuild) {
                if (PackageConst.DefaultLocaleField == Locale.SimplifiedChinese) {
                    return Path.Combine(GetAppDataPath(), "asset-store", "zh", $"executables_{PackageConst.ServerVersion.Replace('.', '-')}");
                }
                return Path.Combine(GetAppDataPath(), "asset-store", $"executables_{PackageConst.ServerVersion.Replace('.', '-')}");
            }
            if (PackageConst.DefaultLocaleField == Locale.SimplifiedChinese) {
                return Path.Combine(GetAppDataPath(), "zh", $"executables_{PackageConst.ServerVersion.Replace('.', '-')}");
            }
            return Path.Combine(GetAppDataPath(), $"executables_{PackageConst.ServerVersion.Replace('.', '-')}");
        }
        
        public static string GetCliTempDir() {
            return Path.Combine(GetHotReloadTempDir(), "MethodPatches");
        }
        
        public static void Chmod(string targetFile, string flags = "+x") {
            // ReSharper disable once PossibleNullReferenceException
            Process.Start(new ProcessStartInfo("chmod", $"{flags} \"{targetFile}\"") {
                UseShellExecute = false,
            }).WaitForExit(2000);
        }
        
        public static bool TryFindServerDir(out string path) {
            const string serverBasePath = "Packages/com.singularitygroup.hotreload/Server";
            if(Directory.Exists(serverBasePath)) {
                path = Path.GetFullPath(serverBasePath);
                return true;
            }
            
            //Not found in packages. Try to find in assets folder.
            //fast path - this is the expected folder
            const string alternativeExecutablePath = "Assets/HotReload/Server";
            if(Directory.Exists(alternativeExecutablePath)) {
                path = Path.GetFullPath(alternativeExecutablePath);
                return true;
            }
            //slow path - try to find the server directory somewhere in the assets folder
            var candidates = Directory.GetDirectories("Assets", "HotReload", SearchOption.AllDirectories);
            foreach(var candidate in candidates) {
                var serverDir = Path.Combine(candidate, "Server");
                if(Directory.Exists(serverDir)) {
                    path = Path.GetFullPath(serverDir);
                    return true;
                }
            }
            path = null;
            return false;
        }
        
        public static string GetPidFilePath(string hotreloadTempDir) {
            return Path.GetFullPath(Path.Combine(hotreloadTempDir, "server.pid"));
        }
        
        public static void KillLastKnownHotReloadProcess() {
            var pidPath = GetPidFilePath(GetHotReloadTempDir());
            try {
                var pid = int.Parse(File.ReadAllText(pidPath));
                Process.GetProcessById(pid).Kill();
            }
            catch {
                //ignore
            }
            File.Delete(pidPath);
        }
    }
}