/**
 * @file    HivePostProcessPushExtension.cs
 * 
 * @author  nanomech
 * @date    2020-2022
 * @copyright Copyright © Com2uS Platform Corporation. All Right Reserved.
 * @defgroup UnityEditor.HiveEditor
 * @{
 * @brief PostPrcessing on BuildTime <br/><br/>
 */

namespace UnityEditor.HiveEditor
{
    using System.IO;
    using System.Linq;
    using System.Collections.Generic;
    using Hive.Unity;
    using UnityEditor;
    using UnityEditor.Build;
    using UnityEditor.Build.Reporting;
    using UnityEditor.Callbacks;
    using UnityEngine;

#if UNITY_IOS
    using UnityEditor.iOS;
    using UnityEditor.iOS.Xcode;
#if UNITY_2017_2_OR_NEWER
    using UnityEditor.iOS.Xcode.Extensions;
#endif
#endif

    public class HivePostProcessPushExtension
    {
        public static readonly string DEFAULT_PROJECT_TARGET_NAME = "Unity-iPhone";
        public static readonly string NOTIFICATION_SERVICE_EXTENSION_TARGET_NAME = "NotificationServiceExtension";
        public static readonly string NOTIFICATION_SERVICE_EXTENSION_OBJECTIVEC_FILENAME = "NotificationService";
        public static readonly string NOTIFICATION_CONTENT_EXTENSION_TARGET_NAME = "NotificationContentExtension";
        public static readonly string NOTIFICATION_CONTENT_EXTENSION_OBJECTIVEC_FILENAME = "NotificationContent";

        private static readonly char DIR_CHAR = Path.DirectorySeparatorChar;
        public static readonly string OS_PLATFORM_LOCATION = "Assets" + DIR_CHAR + "Hive_SDK_v4" + DIR_CHAR + "Platform" + DIR_CHAR + "extension" + DIR_CHAR;
        
        public int callbackOrder => 999;

        // [PostProcessBuild(101)]
        public void OnPostprocessBuild(BuildTarget buildTarget, string path)
        {
            HiveLogger.Log("HivePostProcessPushExtension : OnPostProcessBuild -\n target: "+buildTarget+"\npath: "+path);

            if(buildTarget == BuildTarget.iOS) {				
#if UNITY_IOS
                string projPath = PBXProject.GetPBXProjectPath(path);

                PBXProject proj = new PBXProject();
                proj.ReadFromFile(projPath);


                #if UNITY_2019_3_OR_NEWER
                var mainTargetGUID = proj.GetUnityMainTargetGuid();
                var unityFrameworkTargetGUID = proj.GetUnityFrameworkTargetGuid();
                #else
                var mainTarget = PBXProject.GetUnityTargetName();
                var mainTargetGUID = proj.TargetGuidByName(mainTarget);
                var unityFrameworkTarget = mainTargetGUID;
                #endif

                HivePostProcessSettingIOS settings = new();
                if (settings.hasPushNotificationMediaContents) addServiceExtensionTarget(projPath, proj, mainTargetGUID, path);
                if (settings.hasPushNotificationActionButtons) addContentExtensionTarget(projPath, proj, mainTargetGUID, path);
                RemoveExtensionFilesFromTarget(projPath, proj, unityFrameworkTargetGUID);
#endif
            }
        }

#if UNITY_IOS
        private static void RemoveExtensionFilesFromTarget(string projPath, PBXProject project, string unityFrameworkTargetGuid)
        {
            string[] fileNames = new string[] {
                $"{NOTIFICATION_SERVICE_EXTENSION_OBJECTIVEC_FILENAME}.h",
                $"{NOTIFICATION_SERVICE_EXTENSION_OBJECTIVEC_FILENAME}.m",
                $"{NOTIFICATION_CONTENT_EXTENSION_OBJECTIVEC_FILENAME}.swift"
            };

            foreach (string fileName in fileNames)
            {
                string filePath = "Libraries" + DIR_CHAR + "Hive_SDK_v4" + DIR_CHAR + "Platform" + DIR_CHAR + "extension" + DIR_CHAR + "iOS" + DIR_CHAR + fileName;
                string fileGUID = project.FindFileGuidByProjectPath(filePath);
                if (!string.IsNullOrEmpty(fileGUID))
                {
                    project.RemoveFileFromBuild(unityFrameworkTargetGuid, fileGUID);
                    project.RemoveFile(fileGUID);
                }
            }

            project.WriteToFile(projPath);
        }

        private void addServiceExtensionTarget(string projPath, PBXProject proj, string mainTargetGUID, string path)
        {
            var extensionTargetName = NOTIFICATION_SERVICE_EXTENSION_TARGET_NAME;

            // Make Info.plist
            var exisitingPlistFile = CreateNotificationServiceExtensionPlistFile(path);
            if (exisitingPlistFile)
                return;

            var notificationServiceTargetGUID = PBXProjectExtensions.AddAppExtension(
                proj,
                mainTargetGUID,
                extensionTargetName,
                PlayerSettings.GetApplicationIdentifier(BuildTargetGroup.iOS) + "." + extensionTargetName,
                extensionTargetName + DIR_CHAR + "Info.plist"
            );

            // Copy SourceFiles
            AddNotificationServiceSourceFilesToTarget(proj, notificationServiceTargetGUID, path);

            string[] SYSTEM_FRAMEWORKS_TO_ADD_REQUIRED = {
                "NotificationCenter.framework",
                "UserNotifications.framework",
                "UIKit.framework",
                "SystemConfiguration.framework",
                "CoreGraphics.framework",
                "WebKit.framework",
            };

            foreach(var framework in SYSTEM_FRAMEWORKS_TO_ADD_REQUIRED) {
                proj.AddFrameworkToProject(notificationServiceTargetGUID, framework, false);
            }
    
            proj.SetBuildProperty(notificationServiceTargetGUID, "TARGETED_DEVICE_FAMILY", "1,2");
            proj.SetBuildProperty(notificationServiceTargetGUID, "ARCHS", "$(ARCHS_STANDARD)");
            proj.SetBuildProperty(notificationServiceTargetGUID, "DEVELOPMENT_TEAM", PlayerSettings.iOS.appleDeveloperTeamID);
            proj.SetBuildProperty(notificationServiceTargetGUID, "IPHONEOS_DEPLOYMENT_TARGET", "13.0");
            proj.SetBuildProperty(notificationServiceTargetGUID, "ENABLE_BITCODE", "NO");

            proj.SetBuildProperty(notificationServiceTargetGUID, "FRAMEWORK_SEARCH_PATHS", "$(inherited)");
            proj.AddBuildProperty(notificationServiceTargetGUID, "FRAMEWORK_SEARCH_PATHS", "$(PROJECT_DIR)/Frameworks/Hive_SDK_v4/Plugins/iOS/framework");
            proj.AddBuildProperty(notificationServiceTargetGUID, "FRAMEWORK_SEARCH_PATHS", "$(SRCROOT)/Frameworks/Hive_SDK_v4/Plugins/iOS/framework");
            
            proj.WriteToFile (projPath);
        }

        private void addContentExtensionTarget(string projPath, PBXProject proj, string mainTargetGUID, string path)
        {
            var extensionTargetName = NOTIFICATION_CONTENT_EXTENSION_TARGET_NAME;

            // Make Info.plist
            var exisitingPlistFile = CreateNotificationContentExtensionPlistFile(path);
            if (exisitingPlistFile)
                return;

            var notificationContentTargetGUID = PBXProjectExtensions.AddAppExtension(
                proj,
                mainTargetGUID,
                extensionTargetName,
                PlayerSettings.GetApplicationIdentifier(BuildTargetGroup.iOS) + "." + extensionTargetName,
                extensionTargetName + DIR_CHAR + "Info.plist"
            );

            // Copy SourceFiles
            AddNotificationContentSourceFilesToTarget(proj, notificationContentTargetGUID, path);

            string[] SYSTEM_FRAMEWORKS_TO_ADD_REQUIRED = {
                "NotificationCenter.framework",
                "UserNotifications.framework",
                "UserNotificationsUI.framework",
                "UIKit.framework"
            };

            foreach(var framework in SYSTEM_FRAMEWORKS_TO_ADD_REQUIRED) {
                proj.AddFrameworkToProject(notificationContentTargetGUID, framework, false);
            }
    
            proj.SetBuildProperty(notificationContentTargetGUID, "TARGETED_DEVICE_FAMILY", "1,2");
            proj.SetBuildProperty(notificationContentTargetGUID, "ARCHS", "$(ARCHS_STANDARD)");
            proj.SetBuildProperty(notificationContentTargetGUID, "DEVELOPMENT_TEAM", PlayerSettings.iOS.appleDeveloperTeamID);
            proj.SetBuildProperty(notificationContentTargetGUID, "IPHONEOS_DEPLOYMENT_TARGET", "13.0");
            proj.SetBuildProperty(notificationContentTargetGUID, "ENABLE_BITCODE", "NO");
            proj.SetBuildProperty(notificationContentTargetGUID, "SWIFT_VERSION", "5.0");

            proj.SetBuildProperty(notificationContentTargetGUID, "FRAMEWORK_SEARCH_PATHS", "$(inherited)");
            proj.AddBuildProperty(notificationContentTargetGUID, "FRAMEWORK_SEARCH_PATHS", "$(PROJECT_DIR)/Frameworks/Hive_SDK_v4/Plugins/iOS/framework");
            proj.AddBuildProperty(notificationContentTargetGUID, "FRAMEWORK_SEARCH_PATHS", "$(SRCROOT)/Frameworks/Hive_SDK_v4/Plugins/iOS/framework");
            
            proj.WriteToFile (projPath);
        }

        // Create a .plist file for the NSE
        // NOTE: File in Xcode project is replaced everytime, never appends
        private static bool CreateNotificationServiceExtensionPlistFile(string path)
        {

        #if UNITY_2017_2_OR_NEWER
            var pathToNotificationService = path + DIR_CHAR + NOTIFICATION_SERVICE_EXTENSION_TARGET_NAME;
            Directory.CreateDirectory(pathToNotificationService);

            var notificationServicePlistPath = pathToNotificationService + DIR_CHAR + "Info.plist";
            bool exisiting = File.Exists(notificationServicePlistPath);

            // Read from the hive plist template file.
            var notificationServicePlist = new PlistDocument();
            notificationServicePlist.ReadFromFile(OS_PLATFORM_LOCATION + "iOS" + DIR_CHAR + "NotificationServiceInfo.plist");
            notificationServicePlist.root.SetString("CFBundleShortVersionString", PlayerSettings.bundleVersion);
            notificationServicePlist.root.SetString("CFBundleVersion", PlayerSettings.iOS.buildNumber.ToString());
            notificationServicePlist.WriteToFile(notificationServicePlistPath);
            return exisiting;
        #else
            return true;
        #endif
        }

        private static bool CreateNotificationContentExtensionPlistFile(string path)
        {
            var pathToNotificationContent = path + DIR_CHAR + NOTIFICATION_CONTENT_EXTENSION_TARGET_NAME;
            Directory.CreateDirectory(pathToNotificationContent);

            var notificationContentPlistPath = pathToNotificationContent + DIR_CHAR + "Info.plist";
            bool existing = File.Exists(notificationContentPlistPath);

            // Read from the hive plist template file.
            var notificationContentPlist = new PlistDocument();
            notificationContentPlist.ReadFromFile(OS_PLATFORM_LOCATION + "iOS" + DIR_CHAR + "NotificationContentInfo.plist");
            notificationContentPlist.root.SetString("CFBundleShortVersionString", PlayerSettings.bundleVersion);
            notificationContentPlist.root.SetString("CFBundleVersion", PlayerSettings.iOS.buildNumber.ToString());
            PlistElementDict nsExtensionDict = notificationContentPlist.root["NSExtension"].AsDict();
            PlistElementDict nSExtensionAttributesDict = nsExtensionDict["NSExtensionAttributes"].AsDict();
            PlistElementArray nSExtensionCategoriesArray = nSExtensionAttributesDict["UNNotificationExtensionCategory"].AsArray();
            foreach (string item in categoryValuesForNotificationContentExtensionPlistFile(notificationContentPlist))
                nSExtensionCategoriesArray.AddString(item);
            notificationContentPlist.WriteToFile(notificationContentPlistPath);
            return existing;
        }

        private static List<string> categoryValuesForNotificationContentExtensionPlistFile(PlistDocument defaultFile)
        {
            try
            {
                // 커스텀 카테고리
                HivePostProcessSettingIOS settings = new();
                List<string> customCategories = settings.pushNotificatonActionButtonCategories;

                // 사전정의 카테고리
                List<string> fixedCategories = new();
                List<PlistElement> fixedCategoryValues = defaultFile.root["NSExtension"]["NSExtensionAttributes"]["UNNotificationExtensionCategory"].AsArray().values;
                for (int i = 0; i < fixedCategoryValues.Count; i++)
                    fixedCategories.Add(fixedCategoryValues[i].AsString());
                
                // 중복 제거: 사전정의가 커스텀보다 우선됨 (GCPSDK4-2125)
                List<string> allCategories = fixedCategories;
                foreach (string customCategory in customCategories)
                    if (!fixedCategories.Contains(customCategory))
                        allCategories.Add(customCategory);

                return allCategories;
            }
            catch (System.Exception e)
            {
                HiveLogger.LogError("categoryValuesForNotificationContentExtensionPlistFile failed: " + e.ToString());

                return new();
            }
        }

        // Copies NotificationService.m and .h files into the NotificationServiceExtension folder adds them to the Xcode target
        private static void AddNotificationServiceSourceFilesToTarget(PBXProject project, string extensionGUID, string path)
        {
            var buildPhaseID = project.AddSourcesBuildPhase(extensionGUID);
            foreach (var type in new string[] { "m", "h" }) {
                var nativeFileName = NOTIFICATION_SERVICE_EXTENSION_OBJECTIVEC_FILENAME + "." + type;
                var sourcePath = OS_PLATFORM_LOCATION + "iOS" + DIR_CHAR + nativeFileName;
                var nativeFileRelativeDestination = NOTIFICATION_SERVICE_EXTENSION_TARGET_NAME + "/" + nativeFileName;

                var destPath = path + DIR_CHAR + nativeFileRelativeDestination;
                if (!File.Exists(destPath))
                    FileUtil.CopyFileOrDirectory(sourcePath, destPath);

                var sourceFileGUID = project.AddFile(nativeFileRelativeDestination, nativeFileRelativeDestination, PBXSourceTree.Source);
                project.AddFileToBuildSection(extensionGUID, buildPhaseID, sourceFileGUID);
            }
        }

        // Copies NotificationContent.swift file into the NotificationContentExtension folder adds them to the Xcode target
        private static void AddNotificationContentSourceFilesToTarget(PBXProject project, string extensionGUID, string path)
        {
            var buildPhaseID = project.AddSourcesBuildPhase(extensionGUID);
            var nativeFileName = NOTIFICATION_CONTENT_EXTENSION_OBJECTIVEC_FILENAME + "." + "swift";
            var sourcePath = OS_PLATFORM_LOCATION + "iOS" + DIR_CHAR + nativeFileName;
            var nativeFileRelativeDestination = NOTIFICATION_CONTENT_EXTENSION_TARGET_NAME + "/" + nativeFileName;

            var destPath = path + DIR_CHAR + nativeFileRelativeDestination;
            if (!File.Exists(destPath))
                FileUtil.CopyFileOrDirectory(sourcePath, destPath);

            var sourceFileGUID = project.AddFile(nativeFileRelativeDestination, nativeFileRelativeDestination, PBXSourceTree.Source);
            project.AddFileToBuildSection(extensionGUID, buildPhaseID, sourceFileGUID);
        }
#endif
    }
}
