using System;
using System.Collections.Generic;
using System.IO;
using Alibabacloud.Rum.Unity.Logs;
using UnityEditor;
using UnityEditor.Android;
using UnityEditor.Build;
using UnityEditor.Build.Reporting;
using UnityEngine;

namespace Alibabacloud.Rum.Unity.Editor
{
    /// <summary>
    /// Build processor that handles IL2CPP symbol mapping file generation and upload.
    /// Implements both post-build processing for all platforms and Android-specific gradle processing.
    /// </summary>
    public class SymbolFileBuildProcess : IPostprocessBuildWithReport, IPostGenerateGradleAndroidProject
    {
        // Directory names for symbol files
        internal const string IosSymbolsDir = "alibabacloudSymbols";
        internal const string AndroidSymbolsDir = "symbols";

        // Relative paths to LineNumberMappings.json for different platforms
        internal const string IosLineNumberMappingsLocation =
            "Il2CppOutputProject/Source/il2cppOutput/Symbols/LineNumberMappings.json";

        // Multiple possible locations for Android line number mappings
        private static readonly List<string> AndroidLineNumberMappingsLocations = new()
        {
            "../../IL2CppBackup/il2cppOutput/Symbols/LineNumberMappings.json",
            "src/main/il2CppOutputProject/Source/il2cppOutput/Symbols/LineNumberMappings.json",
        };

        /// <summary>
        /// Callback order - make sure this runs last to capture all build outputs
        /// </summary>
        public int callbackOrder => int.MaxValue;

        /// <summary>
        /// Called after the build completes. Handles iOS symbol file copying.
        /// </summary>
        public void OnPostprocessBuild(BuildReport report)
        {
            
            var options = AlibabacloudOptionsExtensions.GetOrCreate();
            if (options == null)
            {
                return;
            }
            
            if (report.summary.platformGroup == BuildTargetGroup.iOS)
            {
                WriteBuildId(options, report.summary.platformGroup, report.summary.guid.ToString(), 
                    report.summary.outputPath);
            }

            CopySymbols(options, report.summary.platformGroup, report.summary.guid.ToString(),
                report.summary.outputPath);
        }

        /// <summary>
        /// Called after Gradle project generation for Android. Handles Android symbol file copying.
        /// </summary>
        public void OnPostGenerateGradleAndroidProject(string path)
        {
            
            var options = AlibabacloudOptionsExtensions.GetOrCreate();
            if (options == null)
            {
                return;
            }
            

            // Unity doesn't provide a build GUID at this stage, so generate our own
            var buildGuid = Guid.NewGuid().ToString();
            WriteBuildId(options, BuildTargetGroup.Android, buildGuid, path);
            AndroidCopyMappingFile(options, path);
        }

        /// <summary>
        /// Copies Android IL2CPP mapping file to a canonical location in the gradle project.
        /// </summary>
        internal void AndroidCopyMappingFile(AlibabacloudOptions options, string gradleRoot)
        {
            // Only proceed if symbol output is enabled
            if (!options.Enabled || !options.OutputSymbols)
            {
                return;
            }

            // Search for LineNumberMappings.json in known locations
            string srcFilePath = null;
            foreach (var relativePath in AndroidLineNumberMappingsLocations)
            {
                var candidateFilePath = Path.Combine(gradleRoot, relativePath);
                if (File.Exists(candidateFilePath))
                {
                    srcFilePath = candidateFilePath;
                    options.LogInfo("Found IL2CPP mappings at: {0}", relativePath);
                    break;
                }
            }

            // If file not found, log warning and abort
            if (string.IsNullOrEmpty(srcFilePath))
            {
                options.LogWarning("Could not find IL2CPP LineNumberMappings.json file. " +
                                   "Make sure IL2CPP scripting backend is enabled and --emit-source-mapping is set.");
                return;
            }

            // Copy to canonical destination
            var dstDirPath = Path.Combine(gradleRoot, AndroidSymbolsDir);
            var dstFilePath = Path.Combine(dstDirPath, Path.GetFileName(srcFilePath));
            
            try
            {
                if (!Directory.Exists(dstDirPath))
                {
                    Directory.CreateDirectory(dstDirPath);
                }

                File.Copy(srcFilePath, dstFilePath, true);
            }
            catch (Exception e)
            {
                options.LogError("Failed to copy IL2CPP mappings file: {0}\n{1}",e.Message, e.StackTrace);
            }
        }

        /// <summary>
        /// Writes a build ID file for correlating symbol files with specific builds.
        /// </summary>
        internal void WriteBuildId(AlibabacloudOptions options, BuildTargetGroup platformGroup, 
            string buildGuid, string outputPath)
        {
            // Only support Android and iOS
            if (platformGroup is not (BuildTargetGroup.Android or BuildTargetGroup.iOS))
            {
                return;
            }

            if (!options.Enabled || !options.OutputSymbols)
            {
                return;
            }

            // Determine output directory based on platform
            var outputDir = platformGroup switch
            {
                BuildTargetGroup.Android => AndroidSymbolsDir,
                BuildTargetGroup.iOS => IosSymbolsDir,
                _ => ""
            };

            var symbolsDir = Path.Combine(outputPath, outputDir);
            if (!Directory.Exists(symbolsDir))
            {
                Directory.CreateDirectory(symbolsDir);
            }

            // Write build_id file
            var buildIdPath = Path.Combine(symbolsDir, "build_id");
            File.WriteAllText(buildIdPath, buildGuid);

            // For Android, also write to assets directory so it's included in the APK
            if (platformGroup == BuildTargetGroup.Android)
            {
                var androidAssetsDir = Path.Combine(outputPath, "src/main/assets");
                if (!Directory.Exists(androidAssetsDir))
                {
                    Directory.CreateDirectory(androidAssetsDir);
                }
                
                var androidBuildIdPath = Path.Combine(androidAssetsDir, "alibabacloud.buildId");
                File.WriteAllText(androidBuildIdPath, buildGuid);
            }
        }

        /// <summary>
        /// Copies symbol files after build completion (primarily for iOS).
        /// </summary>
        internal void CopySymbols(AlibabacloudOptions options, BuildTargetGroup platformGroup, 
            string buildGuid, string outputPath)
        {
            // Only support Android and iOS
            if (platformGroup is not (BuildTargetGroup.Android or BuildTargetGroup.iOS))
            {
                return;
            }

            if (!options.Enabled || !options.OutputSymbols)
            {
                return;
            }

            switch (platformGroup)
            {
                case BuildTargetGroup.iOS:
                    CopyIosSymbolFiles(options, outputPath);
                    break;
                default:
                    break;
            }
        }

        /// <summary>
        /// Copies iOS IL2CPP mapping file to the symbols directory.
        /// </summary>
        private void CopyIosSymbolFiles(AlibabacloudOptions options, string outputPath)
        {
            var mappingsSrcPath = Path.Combine(outputPath, IosLineNumberMappingsLocation);
            var symbolsDir = Path.Combine(outputPath, IosSymbolsDir);
            var mappingsDestPath = Path.Combine(symbolsDir, "LineNumberMappings.json");

            if (File.Exists(mappingsSrcPath))
            {
                
                if (!Directory.Exists(symbolsDir))
                {
                    Directory.CreateDirectory(symbolsDir);
                }
                
                if (File.Exists(mappingsDestPath))
                {
                    File.Delete(mappingsDestPath);
                }
                
                File.Copy(mappingsSrcPath, mappingsDestPath);
            }
        }
    }
}
