﻿#if UNITY_EDITOR && (UNITY_IOS || UNITY_TVOS)
using System;
using System.IO;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using UnityEngine;
using UnityEditor;
using UnityEditor.Callbacks;
using UnityEditor.iOS.Xcode;
using Debug = UnityEngine.Debug;

public class PostProcessIOS : MonoBehaviour {
    private static string configPath = "Assets/Platform101XP/iOSConfig/";
    private static string podfilePostProcessBuildScriptPath = "Assets/Platform101XP/Editor/PodfilePostProcessScript.rb";

    //must be between 40 and 50 to ensure that it's not overriden by Podfile generation (40) and that it's added before "pod install" (50)
    [PostProcessBuildAttribute(48)]
    private static void PostProcessBuild_iOS(BuildTarget target, string buildPath)
    {
        if (target != BuildTarget.iOS)
        {
            return;
        }

        PostProcessBuildPodfile_iOS(buildPath);
        PostProcessBuildGoogleService_iOS(buildPath);
        PostProcessBuildInfoPlist_iOS(buildPath);
        PostProcessBuildEntitlements_iOS(buildPath);
        PostProcessBuildFrameworks_iOS(buildPath);
        PostProcessUpdatePod_iOS(buildPath);
    }

    private static void PostProcessUpdatePod_iOS(string buildPath)
    {
        try
        {
            Debug.Log("[101XP] Pod installation is starting");
            RunShellCommand("/usr/local/bin/pod", "repo-art update p101xp-stage", buildPath);
            RunShellCommand("/usr/local/bin/pod", "install", buildPath);
            Debug.Log("[101XP] Pod installation is finished");
        }
        catch (Exception ex)
        {
            Debug.LogError(ex);  
        }
    }
    
    private static void RunShellCommand(string command, string arguments, string workingDirectory)
    {
        Process process = new Process();

        process.StartInfo.FileName = command;  // Explicitly using the /usr/local/bin/pod command
        process.StartInfo.Arguments = arguments;
        process.StartInfo.WorkingDirectory = workingDirectory;

        // Set the environment variable for UTF-8 encoding
        process.StartInfo.EnvironmentVariables["LANG"] = "en_US.UTF-8";

        // Redirect output and error streams
        process.StartInfo.RedirectStandardOutput = true;
        process.StartInfo.RedirectStandardError = true;
        process.StartInfo.UseShellExecute = false;
        process.StartInfo.CreateNoWindow = true;

        process.Start();

        // Wait for the process to exit
        process.WaitForExit();

        // Log the output
        Debug.LogFormat("[101XP] {0}", process.StandardOutput.ReadToEnd());

        // Log the error if there is
        string errorOutput = process.StandardError.ReadToEnd();
        if (!string.IsNullOrEmpty(errorOutput))
        {
            Debug.LogErrorFormat("[101XP] {0}", errorOutput);
        }
    }

    private static void PostProcessBuildPodfile_iOS(string buildPath)
    {
        string podfilePostProcessBuildScript = File.ReadAllText(podfilePostProcessBuildScriptPath);
        string path = buildPath + "/Podfile";
        StreamReader streamReader = new StreamReader(path);
        // NOTE: We also remove static linkage (static does not work for FB SDK)
        string podString = "plugin 'cocoapods-art', :sources => [\n\t'p101xp-stage'\n]\n" + 
                           streamReader.ReadToEnd().Replace(":linkage => :static", "") + 
                           podfilePostProcessBuildScript;
        streamReader.Close();
        using (StreamWriter sw = new StreamWriter(path, false))
        {
            sw.Write(podString);
        }
    }

    private static void PostProcessBuildGoogleService_iOS(string buildPath)
    {
        var fileGoogleService = "GoogleService-Info.plist";
        var sourceFileGoogleService = configPath + fileGoogleService;
        if (!File.Exists(sourceFileGoogleService)) {
            Debug.LogFormat("{0} not found", sourceFileGoogleService);
            return;
        }

        // Copy a file
        FileUtil.ReplaceFile(sourceFileGoogleService, buildPath + "/" + fileGoogleService);

        // Add the file to the project
        var project = GetProject(buildPath);
        var targetProject = project.GetUnityMainTargetGuid();
        project.AddFileToBuild(targetProject, project.AddFile(fileGoogleService, fileGoogleService));
        project.WriteToFile(GetProjectPath(buildPath));
    }

    private static void PostProcessBuildInfoPlist_iOS(string buildPath)
    {   
        var sourceP101XPInfo = configPath + "P101XP-Info.plist";
        if (!File.Exists(sourceP101XPInfo)) {
            Debug.LogFormat("{0} not found", sourceP101XPInfo);
            return;
        }
        var p101XPInfo = GetPlistDocument(sourceP101XPInfo);
        var mainInfoPath = buildPath + "/Info.plist";
        var mainInfo = GetPlistDocument(mainInfoPath);
        var mainInfoValues = mainInfo.root.values;

        foreach (var value in p101XPInfo.root.values)
        {
            if (mainInfoValues.ContainsKey(value.Key))
            {
                HandleExistedValue(value.Key, mainInfo.root);
                Debug.LogFormat("That contains {0}", value.Key);
                continue;
            }
            
            mainInfoValues.Add(value);
        }

        EnsureSceneManifest(mainInfo.root);
        mainInfo.WriteToFile(mainInfoPath);
    }

    private static void HandleExistedValue(string key, PlistElementDict root)
    {
        if (key == "UIRequiredDeviceCapabilities") {
            var array = (PlistElementArray)root.values[key];
            array.AddString("gamekit");
            return;
        }

        if (key == "UIApplicationSceneManifest")
        {
            EnsureSceneManifest(root);
        }
    }

    private static void EnsureSceneManifest(PlistElementDict root)
    {
        root.values.Remove("UIApplicationSceneManifest");

        var sceneManifest = root.CreateDict("UIApplicationSceneManifest");
        sceneManifest.SetBoolean("UIApplicationSupportsMultipleScenes", false);

        var sceneConfigurations = sceneManifest.CreateDict("UISceneConfigurations");
        var applicationRole = sceneConfigurations.CreateArray("UIWindowSceneSessionRoleApplication");
        var defaultConfiguration = applicationRole.AddDict();

        defaultConfiguration.SetString("UISceneConfigurationName", "Default Configuration");
        defaultConfiguration.SetString("UISceneDelegateClassName", "SceneDelegate");
    }

    private static PlistDocument GetPlistDocument(string path)
    {
        var plist = new PlistDocument();
        plist.ReadFromFile(path);
        return plist;
    }

    private static void PostProcessBuildEntitlements_iOS(string buildPath)
    {
        var sourceEntitlementsFile = configPath + "P101XP.entitlements";
        if (!File.Exists(sourceEntitlementsFile)) {
            Debug.LogFormat("{0} not found", sourceEntitlementsFile);
            return;
        }
        
        // Copy a file
        string entitlementsFile = PlayerSettings.productName.Replace(" ", "") + ".entitlements";
        FileUtil.ReplaceFile(sourceEntitlementsFile, buildPath + "/" + entitlementsFile);

        // Add the file to the project
        var project = GetProject(buildPath);
        var targetProject = project.GetUnityMainTargetGuid();
        project.AddFile(entitlementsFile, entitlementsFile);
        project.AddBuildProperty(targetProject, "CODE_SIGN_ENTITLEMENTS", entitlementsFile);
        project.WriteToFile(GetProjectPath(buildPath));
    }

    private static void PostProcessBuildFrameworks_iOS(string buildPath)
    {
        var project = GetProject(buildPath);
        var targetProject = project.GetUnityMainTargetGuid();
        project.AddFrameworkToProject(targetProject, "StoreKit.framework", false);
        project.AddFrameworkToProject(targetProject, "GameKit.framework", false);
        project.SetBuildProperty(project.ProjectGuid(), "ENABLE_BITCODE", "NO");
        project.SetBuildProperty(project.GetUnityFrameworkTargetGuid(), "ENABLE_BITCODE", "NO");
        project.AddShellScriptBuildPhase(targetProject, "Upload symbols to FirebaseCrashlytics",
            "", "${PODS_ROOT}/FirebaseCrashlytics/run",
            new List<string>() { "$(SRCROOT)/$(BUILT_PRODUCTS_DIR)/$(INFOPLIST_PATH)" });
        project.WriteToFile(GetProjectPath(buildPath));
    }

    private static PBXProject GetProject(string buildPath)
    {
        PBXProject project = new PBXProject();
        project.ReadFromFile(GetProjectPath(buildPath));
        return project;
    }

    private static string GetProjectPath(string buildPath)
    {
        return buildPath + "/Unity-iPhone.xcodeproj/project.pbxproj";
    }
}
#endif
