using System;
using System.IO;
using System.Runtime.ExceptionServices;
using System.Runtime.InteropServices;
using Alibabacloud.Rum.Unity.Logs;
using Alibabacloud.Rum.Unity.Rum;
using Alibabacloud.Rum.Unity.Worker;
using UnityEngine;
using UnityEngine.Scripting;

[assembly: UnityEngine.Scripting.Preserve]
[assembly: UnityEngine.Scripting.AlwaysLinkAssembly]

namespace Alibabacloud.Rum.Unity.Windows
{
    /// <summary>
    /// Windows platform auto-initialization
    /// </summary>
    [Preserve]
    public static class AlibabacloudWindowsInitialization
    {
        [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
        public static void InitializeAlibabacloud()
        {
            var options = AlibabacloudOptions.LoadAlibabacloudUnityOptions();
            if (options != null && options.Enabled)
            {
                var windowsPlatform = new WindowsPlatform();
                windowsPlatform.Init(options);
                AlibabacloudSdk.InitWithPlatform(windowsPlatform, options);
            }
        }
    }

    /// <summary>
    /// Windows platform implementation
    /// </summary>
    [Preserve]
    public class WindowsPlatform : IAlibabacloudPlatform
    {
        private bool _isInitialized;
        private bool _isClosed;
        private bool _quitHandlerRegistered;

        #region IAlibabacloudPlatform Implementation

        public void Init(AlibabacloudOptions options)
        {
            if (options == null)
            {
                return;
            }

            if (_isInitialized)
            {
                options.LogWarning("[ALIBABACLOUD Windows] Already initialized");
                return;
            }

            // Wrap entire init flow with SEH-aware exception barrier so that
            // any native crash (AccessViolation, stack imbalance, etc.) is
            // surfaced to the Unity log instead of silently terminating the
            // process with no Player.log entry.
            SafeInit(options);
        }

        [HandleProcessCorruptedStateExceptions]
        private void SafeInit(AlibabacloudOptions options)
        {
            try
            {
                // Ensure native DLL is loaded before any P/Invoke calls
                WindowsDllLoader.EnsureDllLoaded();

                // If the native DLL could not be loaded, abort initialization
                // gracefully. WindowsDllLoader has already logged the searched
                // paths or LoadLibrary error code for diagnosis.
                if (!WindowsDllLoader.IsAvailable)
                {
                    options.LogError("[ALIBABACLOUD Windows] Native DLL unavailable, SDK disabled.");
                    return;
                }

                // Create options
                IntPtr optionsPtr = AlibabacloudRumNativeBridge.alibabacloud_rum_options_new();
                if (optionsPtr == IntPtr.Zero)
                {
                    options.LogError("[ALIBABACLOUD Windows] Failed to create options object");
                    return;
                }

                // Log key configuration so the user can verify that the
                // ScriptableObject was actually loaded and contains the
                // expected configAddress / appId values. On Windows Standalone
                // we surface the native SDK terminology (configAddress / appId)
                // instead of Endpoint / ServiceId to match the Editor UI labels
                // (CoreTab.cs) and the alibabacloud_rum_options_set_* APIs.
                // NOTE: LogInfo extension supports up to 3 format args, so we
                // pre-format the message via string.Format to avoid CS1501.
                options.LogInfo(string.Format(
                    "[ALIBABACLOUD Windows] Config configAddress='{0}', appId='{1}', appName='{2}', appVersion='{3}'",
                    string.IsNullOrEmpty(options.Endpoint) ? "<EMPTY>" : options.Endpoint,
                    string.IsNullOrEmpty(options.ServiceId) ? "<EMPTY>" : options.ServiceId,
                    Application.productName,
                    Application.version));

                // Set configuration address (CONFIG ADDRESS in Editor UI)
                if (!string.IsNullOrEmpty(options.Endpoint))
                {
                    AlibabacloudRumNativeBridge.alibabacloud_rum_options_set_config_address(
                        optionsPtr, options.Endpoint);
                }
                else
                {
                    options.LogError("[ALIBABACLOUD Windows] CONFIG ADDRESS is empty; no data will be uploaded.");
                }

                // Set app ID (APP ID in Editor UI)
                if (!string.IsNullOrEmpty(options.ServiceId))
                {
                    AlibabacloudRumNativeBridge.alibabacloud_rum_options_set_app_id(
                        optionsPtr, options.ServiceId);
                }
                else
                {
                    options.LogError("[ALIBABACLOUD Windows] APP ID is empty; uploads will be rejected by the server.");
                }

                // Set app name
                if (!string.IsNullOrEmpty(Application.productName))
                {
                    AlibabacloudRumNativeBridge.alibabacloud_rum_options_set_app_name(
                        optionsPtr, Application.productName);
                }

                // Set app version
                if (!string.IsNullOrEmpty(Application.version))
                {
                    AlibabacloudRumNativeBridge.alibabacloud_rum_options_set_app_version(
                        optionsPtr, Application.version);
                }

                // Set environment
                int env = GetEnvironment(options.EnvironmentOverride);
                AlibabacloudRumNativeBridge.alibabacloud_rum_options_set_env(optionsPtr, env);

                // Set cache path
                string cachePath = GetCachePath();
                if (!string.IsNullOrEmpty(cachePath))
                {
                    AlibabacloudRumNativeBridge.alibabacloud_rum_options_set_cache_path(
                        optionsPtr, cachePath);
                }

                // Disable auto crash tracking on Windows Standalone:
                // The native SEH handler conflicts with Unity's own crash reporter
                // and causes silent crashes (no Player.log entries). Unity's
                // built-in exception capture handles managed crashes already.
                AlibabacloudRumNativeBridge.alibabacloud_rum_options_set_auto_crash_tracking(
                    optionsPtr, 0);

                // Disable auto curl tracking (Unity does not use libcurl directly;
                // network instrumentation is handled via UnityWebRequest/HttpClient)
                AlibabacloudRumNativeBridge.alibabacloud_rum_options_set_auto_curl_tracking(
                    optionsPtr, 0);

                // Set debug level based on Unity debug mode
                // Note: AlibabacloudRumSdkLogLevel uses native SDK values (1-7)
                int debugLevel = options.EnableNativeSDKLogDebug
                    ? (int)AlibabacloudRumSdkLogLevel.Debug
                    : (int)AlibabacloudRumSdkLogLevel.Info;
                AlibabacloudRumNativeBridge.alibabacloud_rum_options_set_debug_level(
                    optionsPtr, debugLevel);

                // Initialize SDK
                int result = AlibabacloudRumNativeBridge.alibabacloud_rum_init(optionsPtr);
                if (result == 0)
                {
                    _isInitialized = true;
                    options.LogInfo("[ALIBABACLOUD Windows] SDK initialized successfully");

                    // NOTE: alibabacloud_rum_init() already calls start_session()
                    // internally (see native_sdk alibabacloud_rum_native.c). Calling
                    // it again here would just end + recreate the session for no
                    // benefit, so we skip it.

                    // Register a process-exit hook so the native dispatcher has a chance
                    // to flush the in-memory event queue before the player exits.
                    // OpenTelemetry BatchSpanProcessor uses schedule_delay = 5s and
                    // batch_size = 20; without an explicit close() on quit, any pending
                    // spans (events / logs / exceptions / resources) are dropped and
                    // nothing reaches the server.
                    RegisterQuitHandler(options);
                }
                else
                {
                    options.LogError("[ALIBABACLOUD Windows] SDK initialization failed with code: {0}", result);
                }

                // NOTE: Do NOT call alibabacloud_rum_options_free(optionsPtr) here.
                // The native SDK takes ownership of `options` after alibabacloud_rum_init,
                // and freeing it again would be a double-free.
            }
            catch (DllNotFoundException e)
            {
                options.LogError("[ALIBABACLOUD Windows] Native DLL not found: {0}", e.Message);
            }
            catch (Exception e)
            {
                options.LogError("[ALIBABACLOUD Windows] Initialization failed: {0}\n{1}", 
                    e.Message, e.StackTrace);
            }
        }

        public AlibabacloudWorker CreateWorker()
        {
            return new ThreadedWorker();
        }

        public IAlibabacloudRum InitRum(AlibabacloudOptions options)
        {
            return new WindowsRum(options);
        }

        #endregion

        #region Lifecycle

        private void RegisterQuitHandler(AlibabacloudOptions options)
        {
            if (_quitHandlerRegistered)
            {
                return;
            }
            _quitHandlerRegistered = true;

            Application.quitting += () =>
            {
                FlushAndClose(options);
            };
        }

        [HandleProcessCorruptedStateExceptions]
        private void FlushAndClose(AlibabacloudOptions options)
        {
            if (!_isInitialized || _isClosed || !WindowsDllLoader.IsAvailable)
            {
                return;
            }
            _isClosed = true;

            try
            {
                // End the active session and close the SDK to force the native
                // dispatcher/transport to flush its in-memory queue synchronously.
                AlibabacloudRumNativeBridge.alibabacloud_rum_end_session();
                int code = AlibabacloudRumNativeBridge.alibabacloud_rum_close();
                options.LogInfo("[ALIBABACLOUD Windows] SDK closed on quit, code={0}", code);
            }
            catch (Exception e)
            {
                options.LogError("[ALIBABACLOUD Windows] FlushAndClose failed: {0}", e.Message);
            }
        }

        #endregion

        #region Helper Methods

        /// <summary>
        /// Get the cache path for SDK data
        /// </summary>
        private static string GetCachePath()
        {
            try
            {
                string cachePath = Path.Combine(
                    Application.persistentDataPath,
                    "AlibabacloudRUM"
                );

                if (!Directory.Exists(cachePath))
                {
                    Directory.CreateDirectory(cachePath);
                }

                return cachePath;
            }
            catch (Exception)
            {
                return string.Empty;
            }
        }

        /// <summary>
        /// Convert environment string to native SDK enum value
        /// </summary>
        private static int GetEnvironment(string env)
        {
            return env?.ToLower() switch
            {
                "prod" => (int)AlibabacloudRumEnv.Prod,
                "gray" => (int)AlibabacloudRumEnv.Gray,
                "pre" => (int)AlibabacloudRumEnv.Pre,
                "daily" => (int)AlibabacloudRumEnv.Daily,
                "local" => (int)AlibabacloudRumEnv.Local,
                _ => (int)AlibabacloudRumEnv.Prod
            };
        }

        #endregion
    }
}
