using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media;
using System.Runtime.InteropServices;
using YMhut.Box.Core.Logging;
using YMhut.Box.Core.App;
using YMhut.Box.Core.Settings;
using YMhut.Box.Core.Startup;
using YMhut.Box.WinUI.Services;
using YMhut.Box.WinUI.Views;

namespace YMhut.Box.WinUI;

public partial class App : Application
{
    private const int MrtNoLoadedMuiItems = unchecked((int)0x80073B01);
    private static int HandlingUnhandledException;

    public static Window? CurrentWindow { get; private set; }
    private static readonly string StartupTracePath = Path.Combine(
        Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
        "YMhut Box",
        "WinUI",
        "Logs",
        "startup-trace.log");

    public App()
    {
        Trace("App.ctor");
        InitializeComponent();
        UnhandledException += App_UnhandledException;
        AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
        TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
    }

    protected override async void OnLaunched(LaunchActivatedEventArgs args)
    {
        try
        {
            Trace("OnLaunched:start");
            if (RuntimeLayoutBootstrapper.TryRelaunchFromWritableLayoutIfNeeded())
            {
                Trace("OnLaunched:relaunched-runtime-layout");
                Current.Exit();
                return;
            }

            if (IndependentWindowHostOptions.TryParse(Environment.GetCommandLineArgs().Skip(1), out var hostOptions))
            {
                Trace($"OnLaunched:independent-host:{hostOptions.Kind}");
                AppServices.Configure(
                    string.IsNullOrWhiteSpace(hostOptions.Root) ? null : hostOptions.Root);
                await LaunchIndependentWindowHostAsync(hostOptions);
                return;
            }

            AppServices.Configure();
            CrashLog.Write("Launch started.");
            var startupInitialization = AppServices.GetRequiredService<IStartupInitializationService>();
            var settingsService = AppServices.GetRequiredService<ISettingsService>();
            var logService = AppServices.GetRequiredService<ILogService>();
            var acceptanceStore = AppServices.GetRequiredService<IAgreementAcceptanceStore>();
            var appVersionService = AppServices.GetRequiredService<IAppVersionService>();
            var startupSplash = new StartupSplashWindow(settingsService.Current.Theme);
            CurrentWindow = startupSplash;
            startupSplash.Activate();
            await Task.Yield();

            StartupInitializationSnapshot startupSnapshot;
            try
            {
                var progress = new Progress<StartupInitializationProgress>(startupSplash.ReportProgress);
                startupSnapshot = await startupInitialization.InitializeAsync(progress);
                startupSplash.ReportOutcome(startupSnapshot);
                startupSplash.ScheduleCloseFallback(TimeSpan.FromSeconds(5));
                if (startupSnapshot.HasVisibleStartupIssue)
                {
                    Trace($"OnLaunched:startup-check-issues:{startupSnapshot.FastPreflightReport.VisibleIssueCount}");
                    CrashLog.Write($"Startup check completed with issues: issues={startupSnapshot.FastPreflightReport.IssueCount}; visible={startupSnapshot.FastPreflightReport.VisibleIssueCount}; critical={startupSnapshot.FastPreflightReport.CriticalIssueCount}");
                }

                await Task.Delay(2000);
            }
            catch (Exception exception)
            {
                startupSplash.ReportFatal(exception);
                await Task.Delay(1200);
                throw;
            }

            var appVersion = appVersionService.GetCurrent().Version;
            if (!await IsAgreementAcceptedCurrentAsync(settingsService, acceptanceStore, logService))
            {
                Trace("OnLaunched:agreement-required");
                var agreementWindow = new AgreementWindow(settingsService, acceptanceStore, appVersion, async () =>
                {
                    LaunchMainWindow();
                    await Task.CompletedTask;
                });
                CurrentWindow = agreementWindow;
                CurrentWindow.Activate();
                startupSplash.Close();
                return;
            }

            LaunchMainWindow(startupSplash);
        }
        catch (Exception exception)
        {
            CrashLog.Write(exception);
            ShowStartupError(
                AppLocalizer.T("YMhut Box 启动失败", "YMhut Box failed to start"),
                AppLocalizer.T($"启动初始化失败：{exception.Message}", $"Startup initialization failed: {exception.Message}"));
            Current.Exit();
        }
    }

    private static async Task<bool> IsAgreementAcceptedCurrentAsync(
        ISettingsService settingsService,
        IAgreementAcceptanceStore acceptanceStore,
        ILogService logService)
    {
        var settings = settingsService.Current;
        if (!AgreementDocument.SettingsMatchCurrent(settings))
        {
            return false;
        }

        try
        {
            var latest = await acceptanceStore.GetLatestAsync().ConfigureAwait(false);
            return latest is not null &&
                string.Equals(latest.Version, AgreementDocument.CurrentVersion, StringComparison.OrdinalIgnoreCase) &&
                latest.Revision == AgreementDocument.CurrentRevision;
        }
        catch (Exception exception)
        {
            CrashLog.Write(exception);
            await logService.WriteAsync(
                "Warning",
                "agreement",
                "声明签署记录读取失败，暂时使用设置文件作为兜底",
                AppLocalizer.SanitizeSensitiveText(exception.Message, 220)).ConfigureAwait(false);
            return true;
        }
    }

    private static void LaunchMainWindow(Window? previousWindow = null)
    {
        CurrentWindow = new MainWindow();
        Trace("OnLaunched:after-window");
        CurrentWindow.Activate();
        previousWindow?.Close();
        Trace("OnLaunched:after-activate");
        CrashLog.Write("Launch completed.");
        LanguagePackService.ScheduleTransientProjectionCleanup();
    }

    private static async Task LaunchIndependentWindowHostAsync(IndependentWindowHostOptions options)
    {
        var settingsService = AppServices.GetRequiredService<ISettingsService>();
        try
        {
            await settingsService.LoadAsync();
        }
        catch (Exception exception)
        {
            CrashLog.Write(exception);
        }

        Trace($"IndependentWindowHost:before-window:{options.Kind}");
        CurrentWindow = options.Kind switch
        {
            IndependentWindowKind.Browser => new SafeBrowserWindow(options.InitialUrl),
            IndependentWindowKind.Media => new MediaPlayerWindow(),
            _ => new SafeBrowserWindow(options.InitialUrl)
        };
        ThemeService.ApplyTheme(CurrentWindow, settingsService.Current.Theme, settingsService.Current.WindowBackdrop);
        CurrentWindow.Activate();
        Trace($"IndependentWindowHost:activated:{options.Kind}");
        CrashLog.Write($"Independent window host launched: {options.Kind}");
    }

    private static void App_UnhandledException(object sender, Microsoft.UI.Xaml.UnhandledExceptionEventArgs e)
    {
        if (Interlocked.Exchange(ref HandlingUnhandledException, 1) == 1)
        {
            e.Handled = true;
            return;
        }

        try
        {
            if (IsBenignResourceProbeException(e.Exception))
            {
                e.Handled = true;
                return;
            }

            TryWriteCrash(e.Exception);
            if (IsRecoverableUiException(e.Exception))
            {
                e.Handled = true;
            }
        }
        catch
        {
            e.Handled = true;
        }
        finally
        {
            Interlocked.Exchange(ref HandlingUnhandledException, 0);
        }
    }

    private static void CurrentDomain_UnhandledException(object sender, System.UnhandledExceptionEventArgs e)
    {
        TryWriteCrash(e.ExceptionObject as Exception ?? new InvalidOperationException(e.ExceptionObject?.ToString()));
    }

    private static void TaskScheduler_UnobservedTaskException(object? sender, UnobservedTaskExceptionEventArgs e)
    {
        TryWriteCrash(e.Exception);
        e.SetObserved();
    }

    private static void TryWriteCrash(Exception? exception)
    {
        try
        {
            CrashLog.Write(exception);
        }
        catch
        {
        }
    }

    private static bool IsRecoverableUiException(Exception? exception)
    {
        if (exception is null)
        {
            return false;
        }

        var details = exception.ToString();
        return exception is System.Runtime.InteropServices.COMException ||
            details.Contains("ToolboxPage", StringComparison.OrdinalIgnoreCase) ||
            details.Contains("MeasureOverride", StringComparison.OrdinalIgnoreCase) ||
            details.Contains("Window object has already been closed", StringComparison.OrdinalIgnoreCase);
    }

    private static bool IsBenignResourceProbeException(Exception? exception)
    {
        if (exception is null)
        {
            return false;
        }

        var details = exception.ToString();
        return exception.HResult == MrtNoLoadedMuiItems ||
            details.Contains("Resource loader cache", StringComparison.OrdinalIgnoreCase) ||
            details.Contains("资源加载器缓存没有已加载的 MUI 项", StringComparison.Ordinal);
    }

    private static string BuildCriticalStartupMessage(StartupCheckReport report)
    {
        var issues = report.Items
            .Where(item => item.IsIssue && item.Severity == StartupCheckSeverity.Critical)
            .Take(8)
            .Select(item => $"- {item.Name}: {item.Detail}{(string.IsNullOrWhiteSpace(item.Path) ? string.Empty : $" ({item.Path})")}");
        return AppLocalizer.T(
            "启动自检发现关键文件或语言资源缺失，请使用最新安装包覆盖安装。\n\n" + string.Join("\n", issues),
            "Startup check found missing critical files or language resources. Please reinstall with the latest setup package.\n\n" + string.Join("\n", issues));
    }

    private static void ShowStartupError(string title, string message)
    {
        try
        {
            _ = MessageBoxW(IntPtr.Zero, message, title, 0x00000010);
        }
        catch
        {
        }
    }

    private static void Trace(string message)
    {
        try
        {
            Directory.CreateDirectory(Path.GetDirectoryName(StartupTracePath)!);
            File.AppendAllText(StartupTracePath, $"{DateTime.Now:O} {message}{Environment.NewLine}");
        }
        catch
        {
        }
    }

    [DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = false)]
    private static extern int MessageBoxW(IntPtr hWnd, string text, string caption, uint type);
}
