using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using UnityEditor;
using UnityEditor.Callbacks;
using UnityEngine;

namespace Topon_Adapter.Editor
{
    [Serializable]
    public sealed class ToponBuildSettings
    {
        public bool enableDebuggerUI = false;
        public bool forceVerbtoUtilVersion = true;
        public string verbtoUtilDependency = ToponBuildSettingsStore.DefaultVerbtoUtilDependency;
        public bool stripResolvedDebuggerArtifacts = true;
        public bool enableIOSDebuggerUI = false;
        public string iosDebuggerPodVersion = ToponBuildSettingsStore.DefaultIOSDebuggerPodVersion;
    }

    internal static class ToponBuildSettingsStore
    {
        public const string DefaultVerbtoUtilDependency = "com.verbto.tools:util:1.1.3";
        public const string IOSDebuggerPodName = "AnyThinkDebugUISDK";
        public const string DefaultIOSDebuggerPodVersion = "1.0.7";

        private const string ActiveBuildSessionKey = "Commercialization.Topon.ActiveBuildSettings";
        private const string SettingsFileSuffix = "_topon_build_settings.json";
        private const string BuildConfigsFolder = "BuildConfigs";
        private const string DebuggerPackageMarker = "com.anythink.sdk:debugger-ui";
        private const string VerbtoPackageMarker = "com.verbto.tools:util";

        [Serializable]
        private sealed class ActiveBuildSettings
        {
            public ToponBuildSettings settings;
            public long utcTicks;
        }

        public static ToponBuildSettings CreateDefault()
        {
            return new ToponBuildSettings();
        }

        public static string GetVerbtoUtilDependency(ToponBuildSettings settings)
        {
            if (settings == null || string.IsNullOrWhiteSpace(settings.verbtoUtilDependency))
            {
                return DefaultVerbtoUtilDependency;
            }

            return settings.verbtoUtilDependency.Trim();
        }

        public static string GetIOSDebuggerPodVersion(ToponBuildSettings settings)
        {
            if (settings == null || string.IsNullOrWhiteSpace(settings.iosDebuggerPodVersion))
            {
                return DefaultIOSDebuggerPodVersion;
            }

            return settings.iosDebuggerPodVersion.Trim();
        }

        public static ToponBuildSettings LoadForProfileName(string profileName, string repositoryRoot)
        {
            var settings = CreateDefault();
            var path = GetSettingsPath(profileName, repositoryRoot);
            if (string.IsNullOrEmpty(path) || !File.Exists(path))
            {
                return settings;
            }

            try
            {
                JsonUtility.FromJsonOverwrite(File.ReadAllText(path), settings);
                Normalize(settings);
            }
            catch (Exception exception)
            {
                Debug.LogWarning($"[TopOn Build] Failed to read build settings: {exception.Message}");
            }

            return settings;
        }

        public static void SaveForProfileName(string profileName, string repositoryRoot, ToponBuildSettings settings)
        {
            if (settings == null)
            {
                settings = CreateDefault();
            }
            else
            {
                Normalize(settings);
            }

            var path = GetSettingsPath(profileName, repositoryRoot);
            if (string.IsNullOrEmpty(path))
            {
                return;
            }

            try
            {
                var directory = Path.GetDirectoryName(path);
                if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
                {
                    Directory.CreateDirectory(directory);
                }

                File.WriteAllText(path, JsonUtility.ToJson(settings, true));
            }
            catch (Exception exception)
            {
                Debug.LogWarning($"[TopOn Build] Failed to save build settings: {exception.Message}");
            }
        }

        public static void SetActiveForCurrentBuild(ToponBuildSettings settings)
        {
            var activeSettings = new ActiveBuildSettings
            {
                settings = Clone(settings),
                utcTicks = DateTime.UtcNow.Ticks
            };

            SessionState.SetString(ActiveBuildSessionKey, JsonUtility.ToJson(activeSettings));
        }

        public static ToponBuildSettings GetActiveForCurrentBuild()
        {
            var json = SessionState.GetString(ActiveBuildSessionKey, string.Empty);
            if (string.IsNullOrWhiteSpace(json))
            {
                return CreateDefault();
            }

            try
            {
                var activeSettings = JsonUtility.FromJson<ActiveBuildSettings>(json);
                if (activeSettings == null || activeSettings.settings == null)
                {
                    return CreateDefault();
                }
                Normalize(activeSettings.settings);

                var activatedAt = new DateTime(activeSettings.utcTicks, DateTimeKind.Utc);
                if (DateTime.UtcNow - activatedAt > TimeSpan.FromHours(6))
                {
                    ClearActiveBuildSettings();
                    return CreateDefault();
                }

                return Clone(activeSettings.settings);
            }
            catch (Exception exception)
            {
                Debug.LogWarning($"[TopOn Build] Failed to read active build settings: {exception.Message}");
                return CreateDefault();
            }
        }

        public static void ClearActiveBuildSettings()
        {
            SessionState.EraseString(ActiveBuildSessionKey);
        }

        public static bool HasStaleDebuggerResolverOutput(string repositoryRoot)
        {
            foreach (var path in GetResolverOutputPaths(repositoryRoot))
            {
                if (!File.Exists(path))
                {
                    continue;
                }

                var content = File.ReadAllText(path);
                if (content.Contains(DebuggerPackageMarker))
                {
                    return true;
                }
            }

            return false;
        }

        public static bool HasUnforcedVerbtoUtilOutput(string repositoryRoot, string expectedDependency)
        {
            expectedDependency = NormalizeDependency(expectedDependency);
            foreach (var path in GetResolverOutputPaths(repositoryRoot))
            {
                if (!File.Exists(path))
                {
                    continue;
                }

                var content = File.ReadAllText(path);
                if (content.Contains(VerbtoPackageMarker) && !content.Contains(expectedDependency))
                {
                    return true;
                }
            }

            foreach (var path in FindResolvedVerbtoUtilArtifacts(repositoryRoot))
            {
                if (!IsExpectedVerbtoUtilArtifactFileName(Path.GetFileName(path), expectedDependency))
                {
                    return true;
                }
            }

            return false;
        }

        public static IReadOnlyList<string> FindResolvedDebuggerArtifacts(string repositoryRoot)
        {
            var result = new List<string>();
            var root = ResolveRepositoryRoot(repositoryRoot);
            if (string.IsNullOrEmpty(root))
            {
                return result;
            }

            var androidPluginPath = Path.Combine(root, "Assets", "Plugins", "Android");
            if (!Directory.Exists(androidPluginPath))
            {
                return result;
            }

            foreach (var path in Directory.GetFiles(androidPluginPath, "*", SearchOption.AllDirectories))
            {
                if (IsDebuggerArtifactFileName(Path.GetFileName(path)))
                {
                    result.Add(path);
                }
            }

            return result;
        }

        public static IReadOnlyList<string> FindResolvedVerbtoUtilArtifacts(string repositoryRoot)
        {
            var result = new List<string>();
            var root = ResolveRepositoryRoot(repositoryRoot);
            if (string.IsNullOrEmpty(root))
            {
                return result;
            }

            var androidPluginPath = Path.Combine(root, "Assets", "Plugins", "Android");
            if (!Directory.Exists(androidPluginPath))
            {
                return result;
            }

            foreach (var path in Directory.GetFiles(androidPluginPath, "*", SearchOption.AllDirectories))
            {
                if (IsVerbtoUtilArtifactFileName(Path.GetFileName(path)))
                {
                    result.Add(path);
                }
            }

            return result;
        }

        internal static bool IsDebuggerArtifactFileName(string fileName)
        {
            if (string.IsNullOrEmpty(fileName))
            {
                return false;
            }

            return fileName.StartsWith("com.anythink.sdk.debugger-ui-", StringComparison.OrdinalIgnoreCase);
        }

        internal static bool IsVerbtoUtilArtifactFileName(string fileName)
        {
            if (string.IsNullOrEmpty(fileName))
            {
                return false;
            }

            return fileName.StartsWith("com.verbto.tools.util-", StringComparison.OrdinalIgnoreCase);
        }

        internal static bool IsExpectedVerbtoUtilArtifactFileName(string fileName, string expectedDependency)
        {
            if (!IsVerbtoUtilArtifactFileName(fileName))
            {
                return false;
            }

            var version = GetVersionFromDependency(expectedDependency);
            if (string.IsNullOrEmpty(version))
            {
                return false;
            }

            return fileName.StartsWith($"com.verbto.tools.util-{version}", StringComparison.OrdinalIgnoreCase);
        }

        private static ToponBuildSettings Clone(ToponBuildSettings settings)
        {
            if (settings == null)
            {
                return CreateDefault();
            }

            return new ToponBuildSettings
            {
                enableDebuggerUI = settings.enableDebuggerUI,
                forceVerbtoUtilVersion = settings.forceVerbtoUtilVersion,
                verbtoUtilDependency = GetVerbtoUtilDependency(settings),
                stripResolvedDebuggerArtifacts = settings.stripResolvedDebuggerArtifacts,
                enableIOSDebuggerUI = settings.enableIOSDebuggerUI,
                iosDebuggerPodVersion = GetIOSDebuggerPodVersion(settings)
            };
        }

        private static void Normalize(ToponBuildSettings settings)
        {
            if (settings == null)
            {
                return;
            }

            settings.verbtoUtilDependency = NormalizeDependency(settings.verbtoUtilDependency);
            settings.iosDebuggerPodVersion = NormalizeIOSDebuggerPodVersion(settings.iosDebuggerPodVersion);
        }

        private static string NormalizeDependency(string dependency)
        {
            if (string.IsNullOrWhiteSpace(dependency))
            {
                return DefaultVerbtoUtilDependency;
            }

            return dependency.Trim();
        }

        private static string NormalizeIOSDebuggerPodVersion(string version)
        {
            if (string.IsNullOrWhiteSpace(version))
            {
                return DefaultIOSDebuggerPodVersion;
            }

            return version.Trim();
        }

        private static string GetVersionFromDependency(string dependency)
        {
            dependency = NormalizeDependency(dependency);
            var parts = dependency.Split(':');
            return parts.Length >= 3 ? parts[parts.Length - 1] : string.Empty;
        }

        private static IEnumerable<string> GetResolverOutputPaths(string repositoryRoot)
        {
            var root = ResolveRepositoryRoot(repositoryRoot);
            if (string.IsNullOrEmpty(root))
            {
                yield break;
            }

            yield return Path.Combine(root, "ProjectSettings", "AndroidResolverDependencies.xml");
            yield return Path.Combine(root, "Assets", "Plugins", "Android", "mainTemplate.gradle");
            yield return Path.Combine(root, "Assets", "Plugins", "Android", "mainTemplate.gradle.backup");
            yield return Path.Combine(root, "Assets", "Plugins", "Android", "settingsTemplate.gradle");
        }

        private static string GetSettingsPath(string profileName, string repositoryRoot)
        {
            var root = ResolveRepositoryRoot(repositoryRoot);
            if (string.IsNullOrEmpty(root))
            {
                return string.Empty;
            }

            return Path.Combine(root, BuildConfigsFolder, $"{SanitizeProfileName(profileName)}{SettingsFileSuffix}");
        }

        private static string ResolveRepositoryRoot(string repositoryRoot)
        {
            if (!string.IsNullOrWhiteSpace(repositoryRoot))
            {
                return Path.GetFullPath(repositoryRoot);
            }

            var dataPath = Application.dataPath;
            var parent = Directory.GetParent(dataPath);
            return parent == null ? string.Empty : parent.FullName;
        }

        private static string SanitizeProfileName(string profileName)
        {
            if (string.IsNullOrWhiteSpace(profileName))
            {
                return "default";
            }

            var invalidChars = Path.GetInvalidFileNameChars();
            var chars = profileName.ToCharArray();
            for (var i = 0; i < chars.Length; i++)
            {
                if (Array.IndexOf(invalidChars, chars[i]) >= 0)
                {
                    chars[i] = '_';
                }
            }

            return new string(chars);
        }
    }

#if UNITY_IOS || UNITY_IPHONE
    internal static class ToponIOSDebuggerDependencyPostProcessor
    {
        private const string Tag = "[TopOn Build]";

        [PostProcessBuild(int.MaxValue)]
        public static void OnPostProcessBuild(BuildTarget buildTarget, string buildPath)
        {
            if (buildTarget != BuildTarget.iOS)
            {
                return;
            }

            var settings = ToponBuildSettingsStore.GetActiveForCurrentBuild();
            try
            {
                ProcessExportedProject(buildPath, settings);
            }
            finally
            {
                ToponBuildSettingsStore.ClearActiveBuildSettings();
            }
        }

        public static void ProcessExportedProject(string buildPath, ToponBuildSettings settings)
        {
            if (settings == null)
            {
                settings = ToponBuildSettingsStore.CreateDefault();
            }

            var podfilePath = Path.Combine(buildPath, "Podfile");
            if (!File.Exists(podfilePath))
            {
                if (settings.enableIOSDebuggerUI)
                {
                    Debug.LogWarning($"{Tag} Podfile not found, cannot add {ToponBuildSettingsStore.IOSDebuggerPodName}: {podfilePath}");
                }

                return;
            }

            var content = File.ReadAllText(podfilePath);
            var updated = RewritePodfile(content, settings);
            if (!string.Equals(content, updated, StringComparison.Ordinal))
            {
                File.WriteAllText(podfilePath, updated);
            }

            if (settings.enableIOSDebuggerUI)
            {
                Debug.Log($"{Tag} iOS DebugUI pod enabled: {ToponBuildSettingsStore.IOSDebuggerPodName} {ToponBuildSettingsStore.GetIOSDebuggerPodVersion(settings)}.");
            }
            else
            {
                Debug.Log($"{Tag} iOS DebugUI pod disabled for this build.");
            }
        }

        private static string RewritePodfile(string content, ToponBuildSettings settings)
        {
            var newline = content.Contains("\r\n") ? "\r\n" : "\n";
            var lines = new List<string>(content.Replace("\r\n", "\n").Replace('\r', '\n').Split('\n'));

            for (var i = lines.Count - 1; i >= 0; i--)
            {
                if (IsDebuggerPodLine(lines[i]))
                {
                    lines.RemoveAt(i);
                }
            }

            if (settings.enableIOSDebuggerUI)
            {
                InsertDebuggerPod(lines, ToponBuildSettingsStore.GetIOSDebuggerPodVersion(settings));
            }

            return string.Join(newline, lines);
        }

        private static bool IsDebuggerPodLine(string line)
        {
            return Regex.IsMatch(line ?? string.Empty,
                @"^\s*pod\s+['""]" + ToponBuildSettingsStore.IOSDebuggerPodName + @"['""]",
                RegexOptions.CultureInvariant);
        }

        private static void InsertDebuggerPod(List<string> lines, string version)
        {
            var targetRange = FindTargetRange(lines, "UnityFramework");
            if (targetRange.start < 0)
            {
                targetRange = FindTargetRange(lines, "Unity-iPhone");
            }

            if (targetRange.start >= 0)
            {
                var indent = ResolvePodIndent(lines, targetRange);
                var podLine = $"{indent}pod '{ToponBuildSettingsStore.IOSDebuggerPodName}', '{version}'";
                lines.Insert(ResolvePodInsertIndex(lines, targetRange), podLine);
                return;
            }

            if (lines.Count > 0 && !string.IsNullOrWhiteSpace(lines[lines.Count - 1]))
            {
                lines.Add(string.Empty);
            }

            lines.Add("target 'UnityFramework' do");
            lines.Add($"  pod '{ToponBuildSettingsStore.IOSDebuggerPodName}', '{version}'");
            lines.Add("end");
        }

        private static (int start, int end) FindTargetRange(List<string> lines, string targetName)
        {
            var targetPattern = new Regex(@"^\s*target\s+['""]" + Regex.Escape(targetName) + @"['""]\s+do\b",
                RegexOptions.CultureInvariant);

            for (var i = 0; i < lines.Count; i++)
            {
                if (!targetPattern.IsMatch(lines[i] ?? string.Empty))
                {
                    continue;
                }

                var depth = 0;
                for (var j = i; j < lines.Count; j++)
                {
                    var line = StripComment(lines[j]);
                    if (Regex.IsMatch(line, @"\bdo\b", RegexOptions.CultureInvariant))
                    {
                        depth++;
                    }

                    if (Regex.IsMatch(line, @"^\s*end\s*$", RegexOptions.CultureInvariant))
                    {
                        depth--;
                        if (depth <= 0)
                        {
                            return (i, j);
                        }
                    }
                }

                return (i, lines.Count);
            }

            return (-1, -1);
        }

        private static int ResolvePodInsertIndex(List<string> lines, (int start, int end) targetRange)
        {
            var insertIndex = targetRange.start + 1;
            for (var i = targetRange.start + 1; i < targetRange.end && i < lines.Count; i++)
            {
                if (Regex.IsMatch(lines[i] ?? string.Empty, @"^\s*pod\s+", RegexOptions.CultureInvariant))
                {
                    insertIndex = i + 1;
                }
            }

            return insertIndex;
        }

        private static string ResolvePodIndent(List<string> lines, (int start, int end) targetRange)
        {
            for (var i = targetRange.start + 1; i < targetRange.end && i < lines.Count; i++)
            {
                var match = Regex.Match(lines[i] ?? string.Empty, @"^(\s*)pod\s+", RegexOptions.CultureInvariant);
                if (match.Success)
                {
                    return match.Groups[1].Value;
                }
            }

            var targetIndent = Regex.Match(lines[targetRange.start] ?? string.Empty, @"^(\s*)", RegexOptions.CultureInvariant).Value;
            return targetIndent + "  ";
        }

        private static string StripComment(string line)
        {
            if (string.IsNullOrEmpty(line))
            {
                return string.Empty;
            }

            var commentIndex = line.IndexOf('#');
            return commentIndex >= 0 ? line.Substring(0, commentIndex) : line;
        }
    }
#endif
}
