#if UNITY_ANDROID
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using UnityEditor;
using UnityEditor.Android;
using UnityEngine;

namespace Tapadn_Adapter.Editor
{
    public sealed class TapadnBuildAndroidProcess : IPostGenerateGradleAndroidProject
    {
        private static readonly XNamespace AndroidNamespace = "http://schemas.android.com/apk/res/android";
        private static readonly XNamespace ToolsNamespace = "http://schemas.android.com/tools";

        public int callbackOrder => 180;

        public void OnPostGenerateGradleAndroidProject(string path)
        {
            ProcessGradleProperties(path);
            ProcessAndroidManifest(path);
        }

        public static void ProcessAndroidManifest(string path)
        {
            var unityManifestPath = Path.Combine(path, "src/main/AndroidManifest.xml");
            var launcherManifestPath = Path.Combine(path, "../launcher/src/main/AndroidManifest.xml");
            var providerTargetManifestPath = File.Exists(launcherManifestPath) ? launcherManifestPath : unityManifestPath;

            if (!File.Exists(unityManifestPath))
            {
                Debug.LogWarning($"[TapADN] AndroidManifest.xml is missing: {unityManifestPath}");
                return;
            }

            var unityManifest = XDocument.Load(unityManifestPath);
            EnsureManifestPatch(unityManifest, true, false);
            unityManifest.Save(unityManifestPath);

            if (File.Exists(providerTargetManifestPath))
            {
                var providerManifest = XDocument.Load(providerTargetManifestPath);
                EnsureManifestPatch(providerManifest, false, true);
                providerManifest.Save(providerTargetManifestPath);
            }

            var resXmlPath = Path.Combine(path, "src/main/res/xml");
            Directory.CreateDirectory(resXmlPath);
            var sourceXmlPath = GetEditorAssetPath("tapad_ad_file_path.xml");
            if (!string.IsNullOrEmpty(sourceXmlPath) && File.Exists(sourceXmlPath))
            {
                File.Copy(sourceXmlPath, Path.Combine(resXmlPath, "tapad_ad_file_path.xml"), true);
            }
        }

        private static void EnsureManifestPatch(XDocument manifest, bool includeWechatActivity, bool includeTapadProvider)
        {
            var manifestElement = manifest.Element("manifest");
            if (manifestElement == null)
            {
                return;
            }

            EnsureNamespace(manifestElement, "tools", ToolsNamespace.NamespaceName);

            EnsurePermission(manifestElement, "android.permission.INTERNET");
            EnsurePermission(manifestElement, "android.permission.ACCESS_NETWORK_STATE");
            EnsurePermission(manifestElement, "android.permission.READ_PHONE_STATE");
            EnsurePermission(manifestElement, "android.permission.QUERY_ALL_PACKAGES");
            EnsurePermission(manifestElement, "android.permission.REQUEST_INSTALL_PACKAGES");
            EnsurePermission(manifestElement, "android.permission.BLUETOOTH");
            EnsurePermission(manifestElement, "android.permission.BLUETOOTH_CONNECT");
            EnsurePermission(manifestElement, "android.permission.ACCESS_FINE_LOCATION");
            EnsurePermission(manifestElement, "android.permission.ACCESS_COARSE_LOCATION");
            EnsurePermission(manifestElement, "android.permission.POST_NOTIFICATIONS");
            EnsureWechatQueries(manifestElement);

            var applicationElement = manifestElement.Element("application");
            if (applicationElement == null)
            {
                applicationElement = new XElement("application");
                manifestElement.Add(applicationElement);
            }

            SetUnityActivitySingleTop(applicationElement);

            if (includeWechatActivity)
            {
                EnsureWechatEntryActivity(applicationElement);
            }

            if (includeTapadProvider)
            {
                EnsureTapadFileProvider(applicationElement);
                SetOrAddAttribute(applicationElement, AndroidNamespace + "allowBackup", "false");
                MergeToolsReplaceAttribute(applicationElement, "android:allowBackup");
            }
        }

        private static void EnsurePermission(XElement manifestElement, string permissionName)
        {
            var exists = manifestElement.Elements("uses-permission")
                .Any(element => element.Attribute(AndroidNamespace + "name")?.Value == permissionName);
            if (exists)
            {
                return;
            }

            var permission = new XElement("uses-permission");
            permission.Add(new XAttribute(AndroidNamespace + "name", permissionName));
            if (permissionName == "android.permission.BLUETOOTH_CONNECT" ||
                permissionName == "android.permission.POST_NOTIFICATIONS")
            {
                permission.Add(new XAttribute(ToolsNamespace + "targetApi", permissionName.EndsWith("CONNECT") ? "s" : "33"));
            }

            manifestElement.Add(permission);
        }

        private static void EnsureWechatQueries(XElement manifestElement)
        {
            var queries = manifestElement.Elements("queries").FirstOrDefault();
            if (queries == null)
            {
                queries = new XElement("queries");
                manifestElement.Add(queries);
            }

            var exists = queries.Elements("package")
                .Any(element => element.Attribute(AndroidNamespace + "name")?.Value == "com.tencent.mm");
            if (!exists)
            {
                var packageElement = new XElement("package");
                packageElement.Add(new XAttribute(AndroidNamespace + "name", "com.tencent.mm"));
                queries.Add(packageElement);
            }
        }

        private static void EnsureWechatEntryActivity(XElement applicationElement)
        {
            RemoveElementsByAndroidName(applicationElement, "activity", ".wxapi.WXEntryActivity");

            var activity = new XElement("activity");
            activity.Add(new XAttribute(AndroidNamespace + "name", ".wxapi.WXEntryActivity"));
            activity.Add(new XAttribute(AndroidNamespace + "label", "@string/app_name"));
            activity.Add(new XAttribute(AndroidNamespace + "theme", "@android:style/Theme.Translucent.NoTitleBar"));
            activity.Add(new XAttribute(AndroidNamespace + "exported", "true"));
            activity.Add(new XAttribute(AndroidNamespace + "taskAffinity", Application.identifier));
            activity.Add(new XAttribute(AndroidNamespace + "launchMode", "singleTop"));
            applicationElement.Add(activity);
        }

        private static void EnsureTapadFileProvider(XElement applicationElement)
        {
            RemoveElementsByAndroidName(applicationElement, "provider", "com.tapsdk.tapad.internal.TapADFileProvider");

            var provider = new XElement("provider");
            provider.Add(new XAttribute(AndroidNamespace + "name", "com.tapsdk.tapad.internal.TapADFileProvider"));
            provider.Add(new XAttribute(AndroidNamespace + "authorities", "${applicationId}.com.tds.ad.fileprovider"));
            provider.Add(new XAttribute(AndroidNamespace + "exported", "false"));
            provider.Add(new XAttribute(AndroidNamespace + "grantUriPermissions", "true"));
            provider.Add(new XAttribute(ToolsNamespace + "replace", "android:authorities"));

            var metadata = new XElement("meta-data");
            metadata.Add(new XAttribute(AndroidNamespace + "name", "android.support.FILE_PROVIDER_PATHS"));
            metadata.Add(new XAttribute(AndroidNamespace + "resource", "@xml/tapad_ad_file_path"));
            provider.Add(metadata);

            applicationElement.Add(provider);
        }

        private static void SetUnityActivitySingleTop(XElement applicationElement)
        {
            foreach (var activity in applicationElement.Elements("activity"))
            {
                var name = activity.Attribute(AndroidNamespace + "name")?.Value;
                if (name == "com.unity3d.player.UnityPlayerActivity")
                {
                    SetOrAddAttribute(activity, AndroidNamespace + "launchMode", "singleTop");
                }
            }
        }

        private static void RemoveElementsByAndroidName(XElement parent, string localName, string androidName)
        {
            parent.Elements(localName)
                .Where(element => element.Attribute(AndroidNamespace + "name")?.Value == androidName)
                .ToList()
                .ForEach(element => element.Remove());
        }

        private static void ProcessGradleProperties(string path)
        {
            var root = Directory.GetParent(path)?.FullName;
            if (string.IsNullOrEmpty(root))
            {
                return;
            }

            var gradlePropertiesPath = Path.Combine(root, "gradle.properties");
            var lines = File.Exists(gradlePropertiesPath)
                ? File.ReadAllLines(gradlePropertiesPath).ToList()
                : new List<string>();

            UpsertProperty(lines, "android.useAndroidX", "true");
            UpsertProperty(lines, "android.enableJetifier", "true");

            File.WriteAllText(gradlePropertiesPath, string.Join("\n", lines) + "\n");
        }

        private static void UpsertProperty(List<string> lines, string key, string value)
        {
            var prefix = key + "=";
            var index = lines.FindIndex(line => line.TrimStart().StartsWith(prefix));
            if (index >= 0)
            {
                lines[index] = prefix + value;
                return;
            }

            lines.Add(prefix + value);
        }

        private static void SetOrAddAttribute(XElement element, XName attributeName, string value)
        {
            var attribute = element.Attribute(attributeName);
            if (attribute == null)
            {
                element.Add(new XAttribute(attributeName, value));
                return;
            }

            attribute.SetValue(value);
        }

        private static void MergeToolsReplaceAttribute(XElement element, params string[] replaceValues)
        {
            var replaceAttributeName = ToolsNamespace + "replace";
            var mergedValues = new List<string>();
            var currentValue = element.Attribute(replaceAttributeName)?.Value;
            if (!string.IsNullOrWhiteSpace(currentValue))
            {
                mergedValues.AddRange(currentValue.Split(',')
                    .Select(value => value.Trim())
                    .Where(value => !string.IsNullOrWhiteSpace(value)));
            }

            foreach (var replaceValue in replaceValues)
            {
                if (!mergedValues.Contains(replaceValue))
                {
                    mergedValues.Add(replaceValue);
                }
            }

            element.SetAttributeValue(replaceAttributeName, string.Join(",", mergedValues));
        }

        private static void EnsureNamespace(XElement element, string prefix, string namespaceName)
        {
            var namespaceAttributeName = XNamespace.Xmlns + prefix;
            if (element.Attribute(namespaceAttributeName) == null)
            {
                element.Add(new XAttribute(namespaceAttributeName, namespaceName));
            }
        }

        private static string GetEditorAssetPath(string fileName)
        {
            var guids = AssetDatabase.FindAssets(Path.GetFileNameWithoutExtension(fileName));
            foreach (var guid in guids)
            {
                var path = AssetDatabase.GUIDToAssetPath(guid);
                if (Path.GetFileName(path) == fileName)
                {
                    return path;
                }
            }

            return null;
        }
    }
}
#endif
