using System.IO;
using System.Windows;
using System.Windows.Threading;
using Microsoft.Extensions.Logging;

namespace DragonISO.App;

// Crash diagnostics â€” the three exception channels WPF leaves open by
// default, wired to a single handler that logs Fatal to Serilog (rolling
// daily file at %LOCALAPPDATA%\Dragon-ISO\Logs) and then shows the user a
// dialog with the log path so they can attach it to a bug report.
//
// We deliberately don't catch StackOverflowException or
// ExecutionEngineException â€” both are uncatchable in modern .NET; if one
// fires the OS Watson dialog takes it from here.
public partial class App
{
    /// <summary>
    /// Where the rolling Serilog file sink writes. Reused by the crash
    /// dialog so we can show the user the exact directory to attach when
    /// filing a bug.
    /// </summary>
    private static string LogDirectory =>
        Path.Combine(
            Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
            "Dragon-ISO", "Logs");

    private void OnAppDomainUnhandled(object sender, UnhandledExceptionEventArgs e)
    {
        // IsTerminating is almost always true here â€” finalizers and
        // managed-thread top-frames don't have a graceful path back. Log
        // + show a dialog inline since the process will exit either way.
        var ex = e.ExceptionObject as Exception;
        TryLogFatal("AppDomain.UnhandledException", ex);
        TryShowCrashDialog(ex, terminating: e.IsTerminating);
    }

    private void OnDispatcherUnhandled(object sender, DispatcherUnhandledExceptionEventArgs e)
    {
        TryLogFatal("Dispatcher.UnhandledException", e.Exception);
        TryShowCrashDialog(e.Exception, terminating: false);
        // Mark Handled so a single bad UI thunk doesn't take the whole app
        // down â€” the user has the dialog and the log; they can choose to
        // keep going.
        e.Handled = true;
    }

    private void OnUnobservedTaskException(object? sender, System.Threading.Tasks.UnobservedTaskExceptionEventArgs e)
    {
        TryLogFatal("TaskScheduler.UnobservedTaskException", e.Exception);
        // Don't show a dialog here â€” these fire from the finalizer thread
        // and tend to be cleanup-time noise, not user-actionable. Log only.
        e.SetObserved();
    }

    private void TryLogFatal(string source, Exception? ex)
    {
        try
        {
            var logger = _loggerFactory?.CreateLogger<App>();
            logger?.LogCritical(ex, "{Source} fired", source);
        }
        catch
        {
            // Logger itself failed (rare â€” disk full, permission denied).
            // Swallow: nothing useful to do, and re-throwing during crash
            // handling makes things worse.
        }
    }

    private static void TryShowCrashDialog(Exception? ex, bool terminating)
    {
        try
        {
            var heading = terminating
                ? "Dragon-ISO encountered an unrecoverable error and will exit."
                : "Dragon-ISO encountered an error.";
            var details = ex?.GetType().Name + ": " + (ex?.Message ?? "(no details)");
            var body =
                heading + "\n\n" +
                details + "\n\n" +
                $"A full diagnostic log has been written to:\n{LogDirectory}\n\n" +
                "Attach the most recent file from that directory to your bug report.";
            MessageBox.Show(body, "Dragon-ISO â€” Error",
                MessageBoxButton.OK, MessageBoxImage.Error);
        }
        catch
        {
            // Even the dialog failed (e.g., during shutdown when the
            // message pump is already gone). Nothing more to do.
        }
    }
}
