#if UNITY_EDITOR

namespace Stove.StoveSDK
{
    using System.IO;
    using System.Text;
    using System.Xml;
    using UnityEditor.Android;
    using UnityEngine;

    public class ChangeAndroidAppManifest : IPostGenerateGradleAndroidProject
    {

        public void OnPostGenerateGradleAndroidProject(string basePath)
        {
            Debug.Log("OnPostGenerateGradleAndroidProject : " + basePath);
           
            var instance = StoveSettings.Instance; //:Load

            string zone = "sandbox";
            Debug.Log("zone : " + zone);

            // If needed, add condition checks on whether you need to run the modification routine.
            // For example, specific configuration/app options enabled
            ChangeGradleProperties(basePath);
            
            /*************************** AGP 8.6.1 ***************************/
            string gradleRootPath = Directory.GetParent(basePath).FullName;
            string launcherPath = Path.Combine(gradleRootPath, "launcher");

            FixNamespaceInBuildGradle(basePath, "com.unity3d.player");
            RemovePackageFromManifest(basePath);
            
            FixNamespaceInBuildGradle(launcherPath, UnityEditor.PlayerSettings.applicationIdentifier);
            RemovePackageFromManifest(launcherPath);
            
            CopyGoogleServicesJson(launcherPath);
            /*****************************************************************/
            
            var androidManifest = new AndroidManifest(GetManifestPath(basePath));
            var strings = new Strings(GetStringsPath(basePath));
            
            // CustomTabsService
            androidManifest.SetCustomTabsService();

            // Add your XML manipulation routines
            androidManifest.SetUsesCleartextTraffic();
            androidManifest.SetDoNotAllowBackup();
            androidManifest.SetEnableOnBackInvokedCallback();
            androidManifest.SetHardwareAcceleration();
            androidManifest.SetStoveZone(zone);

            //@since 2.3.0
            androidManifest.SetLaunchModeSingleTask();

            //@since 2.4.0
            androidManifest.SetStoveUnityPlayerActivity();

            //@since 2.5.0
            //androidManifest.SetExternalWritePermission();

            if (StoveSettings.GoogleEnabled)
            {
                //Google
                androidManifest.AddMetaData("com.stove.auth.google.web_client_id", "@string/google_web_client_id");
                androidManifest.AddMetaData("com.stove.auth.google.web_client_secret", "@string/google_web_client_secret");
                strings.AddValue("google_web_client_id", StoveSettings.GoogleWebClientId);
                strings.AddValue("google_web_client_secret", StoveSettings.GoogleWebClientSecret);

                androidManifest.SetGoogleActivity();
            }

            if (StoveSettings.FacebookEnabled)
            {
                //Facebook
                androidManifest.AddMetaData("com.facebook.sdk.ApplicationId", "@string/facebook_app_id");
                androidManifest.AddMetaData("com.facebook.sdk.ClientToken", "@string/facebook_client_token");
                strings.AddValue("facebook_app_id", StoveSettings.FacebookAppId);
                strings.AddValue("facebook_client_token", StoveSettings.FacebookClientToken);
                strings.AddValue("fb_login_protocol_scheme", "fb" + StoveSettings.FacebookAppId);
            }

            if (StoveSettings.NaverEnabled)
            {
                //Naver
                androidManifest.AddMetaData("com.stove.auth.naver.client_id", "@string/naver_client_id");
                androidManifest.AddMetaData("com.stove.auth.naver.client_secret", "@string/naver_client_secret");
                androidManifest.AddMetaData("com.stove.auth.naver.client_name", "@string/naver_client_name");
                strings.AddValue("naver_client_id", StoveSettings.NaverClientId);
                strings.AddValue("naver_client_secret", StoveSettings.NaverClientSecret);
                strings.AddValue("naver_client_name", StoveSettings.NaverClientName);
            }

            if (StoveSettings.LineEnabled)
            {
                //line
                androidManifest.AddMetaData("com.stove.auth.line.channel_id", "@string/line_channel_id");
                strings.AddValue("line_channel_id", StoveSettings.LineChannelId);
            }

            if(StoveSettings.ForegroundNotificatoinEnabled)
            {
                androidManifest.AddMetaData("com.stove.push.foreground_notification_enabled", "true");
            }
            
            if(StoveSettings.HeadsUpNotificationEnabled)
            {
                androidManifest.AddMetaData("com.stove.push.heads_up_notification_enable", "true");
            }

            if (StoveSettings.SanctionType)
            {
                androidManifest.AddMetaData("com.stove.auth.ui.sanction_type", "1");
            }
            
            if (StoveSettings.CustomChannelEnabled)
            {
                if (!string.IsNullOrEmpty(StoveSettings.PushChannelName))
                {
                    androidManifest.AddMetaData("com.stove.push.channel_name", "@string/push_channel_name");
                    strings.AddValue("push_channel_name", StoveSettings.PushChannelName);
                }

                if (!string.IsNullOrEmpty(StoveSettings.PushChannelDescription))
                {
                    androidManifest.AddMetaData("com.stove.push.channel_description", "@string/push_channel_description");
                    strings.AddValue("push_channel_description", StoveSettings.PushChannelDescription);
                }
            }

            if (!string.IsNullOrEmpty(StoveSettings.PushSmallIcon))
            {
                string pluginManifestPath = Path.Combine(
                    basePath, "unityPlugin.androidlib", "AndroidManifest.xml"
                );
                if (File.Exists(pluginManifestPath))
                {
                    var pluginManifest = new AndroidManifest(pluginManifestPath);
                    pluginManifest.AddMetaDataForResource(
                        "com.stove.push.small_icon",
                        "@drawable/" + StoveSettings.PushSmallIcon
                    );
                    pluginManifest.Save();
                    Debug.Log("small_icon added to unityPlugin.androidlib");
                }
                else
                {
                    // fallback (unityLibrary manifest)
                    Debug.Log("unityPlugin.androidlib manifest not found, fallback to unityLibrary");
                    androidManifest.AddMetaDataForResource("com.stove.push.small_icon", "@drawable/" + StoveSettings.PushSmallIcon);
                }
            }
            
            /** Sign in with Apple **/
            androidManifest.AddMetaData("com.stove.auth.apple.service.id", "@string/apple_service_id");
            strings.AddValue("apple_service_id", "com.stove.mvp.google");

            if (StoveSettings.MarketHuaweiEnabled)
            {
                androidManifest.AddMetaData("com.huawei.hms.client.appid", "@string/huawei_app_id");
                strings.AddValue("huawei_app_id", StoveSettings.HuaweiAppId);
            }

            /** Google Play Games **/
            if (StoveSettings.GooglePlayGamesEnabled)
            {
                androidManifest.AddMetaData("com.google.android.gms.games.APP_ID", "@string/google_play_games_project_id");
                strings.AddValue("google_play_games_project_id", StoveSettings.GooglePlayGamesProjectId);
                
                /** For Graphic Test **/
                //androidManifest.SetGooglePlayGamesOpenGL();
            }

            /** ONEstoreBuildType **/
            string ONEstoreBuildType = StoveSettings.ONEstoreBuildType;
            if (ONEstoreBuildType == "Korea")
            {
                androidManifest.AddMetaData("onestore:dev_option", "onestore_00");
            }
            else if (ONEstoreBuildType == "Singapore")
            {
                androidManifest.AddMetaData("onestore:dev_option", "onestore_01");
            }
            else
            {
                androidManifest.AddMetaData("onestore:dev_option", "onestore_02");
            }

            androidManifest.Save();
            strings.Save();
        }

        private void ChangeGradleProperties(string path)
        {
            string gradlePropertiesFile = path + "/gradle.properties";
            if (File.Exists(gradlePropertiesFile))
            {
                File.Delete(gradlePropertiesFile);
            }
            StreamWriter writer = File.CreateText(gradlePropertiesFile);
            writer.WriteLine("android.useAndroidX=true");
            writer.WriteLine("android.enableJetifier=true");
            writer.Flush();
            writer.Close();
        }
        
        private void FixNamespaceInBuildGradle(string basePath, string namespaceName)
        {
            string gradlePath = Path.Combine(basePath, "build.gradle");
            if (!File.Exists(gradlePath))
            {
                Debug.Log("build.gradle not founded.");
                return;
            }

            string content = File.ReadAllText(gradlePath);

            var match = System.Text.RegularExpressions.Regex.Match(
                content, @"namespace\s+'([^']+)'"
            );

            if (match.Success)
            {
                string current = match.Groups[1].Value;
                if (current == namespaceName)
                {
                    Debug.Log($"namespace already correct '{namespaceName}', skip: {gradlePath}");
                    return;
                }
                // 기존 namespace 값을 교체
                content = content.Replace(match.Value, $"namespace '{namespaceName}'");
                Debug.Log($"namespace '{current}' → '{namespaceName}': {gradlePath}");
            }
            else
            {
                // namespace 자체가 없으면 android { 뒤에 추가
                content = System.Text.RegularExpressions.Regex.Replace(
                    content,
                    @"(android\s*\{)",
                    $"$1\n    namespace '{namespaceName}'"
                );
                Debug.Log("namespace added.");
            }

            File.WriteAllText(gradlePath, content);
        }

        private void RemovePackageFromManifest(string basePath)
        {
            string manifestPath = Path.Combine(basePath, "src", "main", "AndroidManifest.xml");
            if (!File.Exists(manifestPath))
                manifestPath = Path.Combine(basePath, "AndroidManifest.xml");
            if (!File.Exists(manifestPath))
            {
                Debug.Log("AndroidManifest.xml not founded.");
                return;
            }

            string content = File.ReadAllText(manifestPath);
            if (!content.Contains("package=")) return;

            content = System.Text.RegularExpressions.Regex.Replace(
                content,
                @"\s*package=""[^""]*""",
                ""
            );

            File.WriteAllText(manifestPath, content);
            Debug.Log("package name removed.");
        }
        
        private void CopyGoogleServicesJson(string launcherPath)
        {
            string[] candidates = new string[]
            {
                Path.Combine(UnityEngine.Application.dataPath, "Plugins", "Android", "google-services.json"),
                Path.Combine(UnityEngine.Application.dataPath, "google-services.json"),
            };

            string src = null;
            foreach (var candidate in candidates)
            {
                if (File.Exists(candidate))
                {
                    src = candidate;
                    break;
                }
            }

            if (src == null)
            {
                Debug.Log("google-services.json not founded.\n" + string.Join("\n", candidates));
                return;
            }

            string dest = Path.Combine(launcherPath, "google-services.json");
            File.Copy(src, dest, overwrite: true);
            Debug.Log($"google-services.json copied. {src} → {dest}");

            if (File.Exists(dest))
                Debug.Log("google-services.json copied OK: " + dest);
            else
                Debug.Log("google-services.json copied FAIL: " + dest);
        }

        public int callbackOrder { get { return 1; } }

        private string _manifestFilePath;

        private string GetManifestPath(string basePath)
        {
            if (string.IsNullOrEmpty(_manifestFilePath))
            {
                var pathBuilder = new StringBuilder(basePath);
                pathBuilder.Append(Path.DirectorySeparatorChar).Append("src");
                pathBuilder.Append(Path.DirectorySeparatorChar).Append("main");
                pathBuilder.Append(Path.DirectorySeparatorChar).Append("AndroidManifest.xml");
                _manifestFilePath = pathBuilder.ToString();
            }
            return _manifestFilePath;
        }

        private string _stringsFilePath;

        private string GetStringsPath(string basePath)
        {
            if (string.IsNullOrEmpty(_stringsFilePath))
            {
                var pathBuilder = new StringBuilder(basePath);
                pathBuilder.Append(Path.DirectorySeparatorChar).Append("src");
                pathBuilder.Append(Path.DirectorySeparatorChar).Append("main");
                pathBuilder.Append(Path.DirectorySeparatorChar).Append("res");
                pathBuilder.Append(Path.DirectorySeparatorChar).Append("values");
                pathBuilder.Append(Path.DirectorySeparatorChar).Append("strings.xml");
                _stringsFilePath = pathBuilder.ToString();
                if (!File.Exists(_stringsFilePath))
                {
                    Debug.Log(_stringsFilePath + " not exist. create");
                    StreamWriter writer = File.CreateText(_stringsFilePath);
                    writer.WriteLine("<?xml version='1.0' encoding='utf-8'?>");
                    writer.WriteLine("<resources>");
                    writer.WriteLine("</resources>");
                    writer.Flush();
                    writer.Close();
                }
            }
            return _stringsFilePath;
        }
    }


    internal class AndroidXmlDocument : XmlDocument
    {
        private string m_Path;
        protected XmlNamespaceManager nsMgr;
        public readonly string AndroidXmlNamespace = "http://schemas.android.com/apk/res/android";
        public AndroidXmlDocument(string path)
        {
            m_Path = path;
            using (var reader = new XmlTextReader(m_Path))
            {
                reader.Read();
                Load(reader);
            }
            nsMgr = new XmlNamespaceManager(NameTable);
            nsMgr.AddNamespace("android", AndroidXmlNamespace);
        }

        public string Save()
        {
            return SaveAs(m_Path);
        }

        public string SaveAs(string path)
        {
            using (var writer = new XmlTextWriter(path, new UTF8Encoding(false)))
            {
                writer.Formatting = Formatting.Indented;
                Save(writer);
            }
            return path;
        }
    }

    internal class AndroidManifest : AndroidXmlDocument
    {
        private readonly XmlElement ApplicationRootElement;
        private readonly XmlElement ApplicationElement;

        public AndroidManifest(string path) : base(path)
        {
            ApplicationRootElement = SelectSingleNode("/manifest") as XmlElement;
            ApplicationElement = SelectSingleNode("/manifest/application") as XmlElement;
        }

        private XmlAttribute CreateAndroidAttribute(string key, string value)
        {
            XmlAttribute attr = CreateAttribute("android", key, AndroidXmlNamespace);
            attr.Value = value;
            return attr;
        }

        public void AddMetaData(string name, string value)
        {
            XmlNode metaData = CreateNode(XmlNodeType.Element, "meta-data", null);
            metaData.Attributes.Append(CreateAttribute("android", "name", "http://schemas.android.com/apk/res/android")).Value = name;
            metaData.Attributes.Append(CreateAttribute("android", "value", "http://schemas.android.com/apk/res/android")).Value = value;
            ApplicationElement.AppendChild(metaData);
        }

        public void AddMetaDataForResource(string name, string value)
        {
            XmlNode metaData = CreateNode(XmlNodeType.Element, "meta-data", null);
            metaData.Attributes.Append(CreateAttribute("android", "name", "http://schemas.android.com/apk/res/android")).Value = name;
            metaData.Attributes.Append(CreateAttribute("android", "resource", "http://schemas.android.com/apk/res/android")).Value = value;
            ApplicationElement.AppendChild(metaData);
        }

        internal void SetGoogleActivity()
        {
            var ns = "http://schemas.android.com/apk/res/android";

            XmlElement activityElement = CreateElement("activity");
            activityElement.SetAttribute("name", ns , "com.stove.auth.google.CustomTabActivity");
            activityElement.SetAttribute("exported", ns, "true");

            XmlElement intentFilter = CreateElement("intent-filter");

            var action = CreateElement("action");
            action.SetAttribute("name", ns, "android.intent.action.VIEW");
            intentFilter.AppendChild(action);

            var category = CreateElement("category");
            category.SetAttribute("name", ns, "android.intent.category.DEFAULT");
            intentFilter.AppendChild(category);

            var category2 = CreateElement("category");
            category2.SetAttribute("name", ns, "android.intent.category.BROWSABLE");
            intentFilter.AppendChild(category2);

            XmlElement dataElement = CreateElement("data");
            dataElement.SetAttribute("scheme", ns, "stove-${applicationId}");
            intentFilter.AppendChild(dataElement);

            activityElement.AppendChild(intentFilter);
            ApplicationElement.AppendChild(activityElement);
        }
        
        internal void SetStoveZone(string zone)
        {
            AddMetaData("com.stove.environment", zone);
        }

        internal void SetUsesCleartextTraffic()
        {
            ApplicationElement.Attributes.Append(CreateAndroidAttribute("usesCleartextTraffic", "true"));
        }
        
        internal void SetDoNotAllowBackup()
        {
            ApplicationElement.Attributes.Append(CreateAndroidAttribute("allowBackup", "false"));
        }

        internal void SetEnableOnBackInvokedCallback()
        {
            ApplicationElement.Attributes.Append(CreateAndroidAttribute("enableOnBackInvokedCallback", "true"));
        }
        
        internal void SetHardwareAcceleration()
        {
            ApplicationElement.Attributes.Append(CreateAndroidAttribute("hardwareAccelerated", "true"));
        }

        internal void SetCustomTabsService()
        {   
            var ns = "http://schemas.android.com/apk/res/android";
            
            XmlElement queriesElement = CreateElement("queries");
            XmlElement intentElement = CreateElement("intent");

            var action = CreateElement("action");
            action.SetAttribute("name", ns, "android.support.customtabs.action.CustomTabsService");
            intentElement.AppendChild(action);

            queriesElement.AppendChild(intentElement);
            ApplicationRootElement.AppendChild(queriesElement);
        }

        internal void SetLaunchModeSingleTask()
        {
            XmlNode xmlNode = GetActivityWithLaunchIntent();
            xmlNode.Attributes.Append(CreateAttribute("android", "launchMode", "http://schemas.android.com/apk/res/android")).Value = "singleTask";
        }

        internal const string ANDROID_NS = "http://schemas.android.com/apk/res/android";
        internal const string TOOLS_NS   = "http://schemas.android.com/tools";

        internal void SetStoveUnityPlayerActivity()
        {
            XmlNode activity = GetActivityWithLaunchIntent();
            activity.Attributes.Append(CreateAttribute("android", "name", "http://schemas.android.com/apk/res/android")).Value = "com.stove.unity.StoveUnityPlayerActivity";
            activity.Attributes.Append(CreateAttribute("android", "exported", "http://schemas.android.com/apk/res/android")).Value = "true";


            // ★ 하드웨어 가속 ON
            activity.Attributes.Append(CreateAttribute("android", "hardwareAccelerated", ANDROID_NS)).Value
                = "true";

            // ★ 라이브러리/AAR가 false로 덮어쓰는 걸 방지
            // tools 네임스페이스 선언(없으면 추가)
            var manifest = activity.OwnerDocument.DocumentElement;
            if (manifest.GetAttribute("xmlns:tools") != TOOLS_NS)
                manifest.SetAttribute("xmlns:tools", TOOLS_NS);

            // tools:replace="android:hardwareAccelerated"
            // (이 속성은 해당 엘리먼트에 붙여야 효력이 있습니다)
            activity.Attributes.Append(CreateAttribute("tools", "replace", TOOLS_NS)).Value
                = "android:hardwareAccelerated";

            // (선택) application 기본값도 true로 보강
            var app = manifest.SelectSingleNode("/manifest/application") as XmlElement;
            app?.SetAttribute("hardwareAccelerated", ANDROID_NS, "true");
        }

        /*        
                internal void SetExternalWritePermission()
                {
                    XmlElement permission = CreateElement("uses-permission");
                    XmlAttribute newAttribute = CreateAndroidAttribute("name", "android.permission.WRITE_EXTERNAL_STORAGE");
                    permission.Attributes.Append(newAttribute);
                    ApplicationRootElement.AppendChild(permission);
                }
        */

        /*
                internal void SetGooglePlayGamesOpenGL()
                {
                    XmlElement feature = CreateElement("uses-feature");
                    feature.Attributes.Append(CreateAndroidAttribute("glEsVersion", "0x00030000"));
                    feature.Attributes.Append(CreateAndroidAttribute("required", "true"));
                    ApplicationRootElement.AppendChild(feature);
                }
        */

        internal XmlNode GetActivityWithLaunchIntent()
        {
            return
                SelectSingleNode(
                    "/manifest/application/activity[intent-filter/action/@android:name='android.intent.action.MAIN' and "
                    + "intent-filter/category/@android:name='android.intent.category.LAUNCHER']",
                    nsMgr);
        }
    }

    internal class Strings : AndroidXmlDocument
    {
        private readonly XmlElement ApplicationElement;

        public Strings(string path) : base(path)
        {
            ApplicationElement = SelectSingleNode("resources") as XmlElement;
        }

        public void AddValue(string name, string value)
        {
            XmlElement element = CreateElement("string");
            element.InnerText = value;
            element.SetAttribute("name", name);
            element.SetAttribute("translatable", "false");
            ApplicationElement.AppendChild(element);
        }
    }
}
#endif
