using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
using Avalonia.Threading;
using Predictalytics.Hosting;
using Predictalytics.Shell.ViewModels;
using Predictalytics.Shell.Views;
using Serilog;

namespace Predictalytics.Shell;

/// <summary>
/// Avalonia-Anwendungsobjekt.
/// <para>
/// Die Basisklasse wird voll qualifiziert angegeben: innerhalb von
/// <c>Predictalytics.*</c> loest der kurze Name <c>Application</c> auf den
/// Namespace <c>Predictalytics.Application</c> auf, nicht auf den Avalonia-Typ.
/// </para>
/// </summary>
public partial class App : global::Avalonia.Application
{
    private IDisposable? _licenseWatch;

    public override void Initialize() => AvaloniaXamlLoader.Load(this);

    public override void OnFrameworkInitializationCompleted()
    {
        if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
        {
            // Das Lizenzfenster darf beim Schliessen nicht die Anwendung beenden —
            // danach soll ja erst das Hauptfenster kommen.
            desktop.ShutdownMode = ShutdownMode.OnExplicitShutdown;
            desktop.Exit += (_, _) => _licenseWatch?.Dispose();

            // Nicht awaiten: OnFrameworkInitializationCompleted muss zurueckkehren,
            // damit die Nachrichtenschleife anlaufen kann.
            Dispatcher.UIThread.Post(async () => await StartupAsync(desktop));
        }

        base.OnFrameworkInitializationCompleted();
    }

    private async Task StartupAsync(IClassicDesktopStyleApplicationLifetime desktop)
    {
        RuntimeSetup.UseInvariantCulture();

        // Einstellungen zuerst: das Fehler-Reporting braucht das Token, bevor die
        // Lizenzschranke laeuft — sonst taucht eine fehlgeschlagene Aktivierung
        // nirgends im Fehler-Stream auf.
        var options = PredictalyticsOptions.Load();

        var viewModel = new MainWindowViewModel(options);
        var window = new MainWindow(viewModel);

        // Serilog-Aufbau liegt in Predictalytics.Hosting; hier kommt nur der
        // Terminal-Sink dazu, der selbst auf den UI-Thread marshallt.
        LoggingSetup.Configure(viewModel.AppendLog);

        // ─── Deployment Center: Fehler-Stream und globale Ausnahmebehandlung ───
        DcErrorReporter.Configure(options.DcToken, options.DcErrorReportingEnabled);
        DcErrorReporter.InstallGlobalHandlers();
        InstallUiThreadHandler();

        // Ohne das erschiene jede Aktivierung in der Lizenzliste als "1.0.0".
        Deploymentcenter.Client.LicenseClient.DefaultAppVersion = DcConfig.AppVersion;

        LoggingSetup.LogStartupBanner();

        // ─── Lizenzschranke: ohne nutzbare Lizenz keine Anwendung ───
        var client = LicenseGuard.CreateClient();
        var check = await LicenseGuard.TryUseCachedAsync(client);
        var session = check.Session;

        if (session is null)
        {
            var licenseWindow = new LicenseWindow(client, check.LastResult);
            desktop.MainWindow = licenseWindow;
            licenseWindow.Show();

            session = await licenseWindow.Completion;
            if (session is null)
            {
                desktop.Shutdown();
                return;
            }
        }

        viewModel.License = session;

        // ─── Hauptfenster ───
        desktop.MainWindow = window;
        desktop.ShutdownMode = ShutdownMode.OnMainWindowClose;
        window.Show();

        viewModel.Start();

        // Waehrend des Betriebs alle 12 h nachpruefen (Widerruf, Ablauf, Offline-Frist).
        _licenseWatch = LicenseGuard.StartPeriodicRevalidation(session, unusable =>
            Dispatcher.UIThread.Post(async () =>
            {
                await Dialogs.ShowErrorAsync(window, "Lizenzfehler",
                    $"Die Lizenz ist nicht mehr gültig ({unusable.Status}):\n{unusable.Message}\n\nPredictalytics wird beendet.");
                desktop.Shutdown();
            }));

        desktop.Exit += (_, _) =>
        {
            Log.Information("Application shutting down.");
            Log.CloseAndFlush();
        };
    }

    /// <summary>
    /// Gegenstueck zu <c>Application.ThreadException</c> in WinForms: eine im UI-Thread
    /// durchgereichte Ausnahme wuerde den Prozess sonst spurlos beenden.
    /// </summary>
    private static void InstallUiThreadHandler()
    {
        Dispatcher.UIThread.UnhandledException += (_, e) =>
        {
            DcErrorReporter.ReportUiThreadException(e.Exception);
            e.Handled = true;
        };
    }
}
