using System.IO;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Runtime.InteropServices;
using System.Windows;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using PSLauncher.App.Services;
using PSLauncher.App.ViewModels;
using PSLauncher.App.Views;
using PSLauncher.Core.Configuration;
using PSLauncher.Core.Downloads;
using PSLauncher.Core.Installations;
using PSLauncher.Core.Integrity;
using PSLauncher.Core.Licensing;
using PSLauncher.Core.Localization;
using PSLauncher.Core.Manifests;
using PSLauncher.Core.DocTool;
using PSLauncher.Core.Migrations;
using PSLauncher.Core.Process;
using PSLauncher.Core.ReportTool;
using PSLauncher.Core.Updates;
using PSLauncher.Models;
using Serilog;

namespace PSLauncher.App;

public partial class App : Application
{
    private IHost? _host;
    private static System.Threading.Mutex? _singleInstanceMutex;

    private const string SingleInstanceMutexName = "Global\\PSLauncher-3F8E2C1A-9B47-4D5E-A0F8-2E9D1B6C7A3F";
    private const string BringToFrontMessage = "PSLAUNCHER_BRING_TO_FRONT";
    private static readonly int WM_PSLAUNCHER_BRING = RegisterWindowMessage(BringToFrontMessage);

    [DllImport("user32.dll")]
    private static extern int RegisterWindowMessage(string lpString);

    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool PostMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);

    private const int HWND_BROADCAST = 0xFFFF;

    public static string LogsDirectory { get; private set; } = string.Empty;

    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);

        // Single-instance : si une autre instance tourne déjà, on lui demande de
        // se mettre au premier plan via un broadcast Windows et on quitte tout de suite.
        _singleInstanceMutex = new System.Threading.Mutex(initiallyOwned: true,
            name: SingleInstanceMutexName, out var createdNew);
        if (!createdNew)
        {
            PostMessage((IntPtr)HWND_BROADCAST, WM_PSLAUNCHER_BRING, IntPtr.Zero, IntPtr.Zero);
            Shutdown();
            return;
        }

        // Logs avant tout — pour ne rien perdre des erreurs au boot
        LogsDirectory = Path.Combine(
            Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
            "PSLauncher", "logs");
        Directory.CreateDirectory(LogsDirectory);

        // Bootstrap i18n : on lit d'abord la config (sans services DI) pour fixer la
        // langue avant que le moindre élément XAML soit instancié.
        try
        {
            var bootstrapStore = new ConfigStore(
                Microsoft.Extensions.Logging.Abstractions.NullLogger<ConfigStore>.Instance);
            var bootstrapCfg = bootstrapStore.Load();
            Strings.Init(bootstrapCfg.Language);
        }
        catch
        {
            Strings.Init("auto");
        }

        Log.Logger = new LoggerConfiguration()
            .MinimumLevel.Debug()
            .Enrich.FromLogContext()
            .WriteTo.Debug()
            .WriteTo.File(
                path: Path.Combine(LogsDirectory, "app-.log"),
                rollingInterval: RollingInterval.Day,
                retainedFileCountLimit: 10,
                outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} [{Level:u3}] {SourceContext} {Message:lj}{NewLine}{Exception}")
            .CreateLogger();

        Log.Information("PSLauncher starting (logs in {Path})", LogsDirectory);

        AppDomain.CurrentDomain.UnhandledException += (_, args) =>
            Log.Fatal((Exception)args.ExceptionObject, "Unhandled exception (AppDomain)");
        DispatcherUnhandledException += (_, args) =>
        {
            Log.Error(args.Exception, "Unhandled UI exception");
            args.Handled = false;
        };

        _host = Host.CreateDefaultBuilder()
            .UseSerilog()
            .ConfigureServices((_, services) =>
            {
                services.AddSingleton<IConfigStore, ConfigStore>();
                services.AddSingleton<LocalConfig>(sp => sp.GetRequiredService<IConfigStore>().Load());

                services.AddSingleton(sp =>
                {
                    // Handler tuné pour les gros téléchargements parallèles :
                    // - MaxConnectionsPerServer=16 pour autoriser le multi-segment (8 par défaut).
                    // - AutomaticDecompression sur l'API JSON uniquement (None ici car les builds
                    //   sont des ZIPs déjà compressés ; éviter la CPU+RAM gaspillée par un éventuel
                    //   wrapping gzip côté serveur).
                    // - PooledConnectionLifetime court pour éviter qu'OVH ferme nos sockets sous nous.
                    var handler = new SocketsHttpHandler
                    {
                        PooledConnectionLifetime = TimeSpan.FromMinutes(5),
                        PooledConnectionIdleTimeout = TimeSpan.FromSeconds(60),
                        MaxConnectionsPerServer = 16,
                        EnableMultipleHttp2Connections = true,
                        AutomaticDecompression = System.Net.DecompressionMethods.None,
                    };
                    var http = new HttpClient(handler)
                    {
                        Timeout = Timeout.InfiniteTimeSpan,
                        DefaultRequestVersion = System.Net.HttpVersion.Version20,
                        DefaultVersionPolicy = HttpVersionPolicy.RequestVersionOrLower,
                    };
                    http.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("PSLauncher", "0.5"));
                    return http;
                });

                services.AddSingleton<IInstallationRegistry>(sp =>
                    new InstallationRegistry(
                        sp.GetRequiredService<ILogger<InstallationRegistry>>(),
                        () => sp.GetRequiredService<LocalConfig>().InstallRoot));

                services.AddSingleton<IProcessLauncher, ProcessLauncher>();
                services.AddSingleton<IIntegrityService, IntegrityService>();
                services.AddSingleton<IZipInstaller, ZipInstaller>();
                services.AddSingleton<IDownloadStateStore, DownloadStateStore>();

                services.AddSingleton<IManifestService>(sp =>
                    new ManifestService(
                        sp.GetRequiredService<HttpClient>(),
                        () => sp.GetRequiredService<LocalConfig>().ServerBaseUrl,
                        sp.GetRequiredService<ILogger<ManifestService>>()));

                services.AddSingleton<IDownloadManager, DownloadManager>();
                services.AddSingleton<IUpdateChecker, UpdateChecker>();
                services.AddSingleton<ILauncherSelfUpdater, LauncherSelfUpdater>();

                services.AddSingleton<ILicenseService>(sp =>
                    new LicenseService(
                        sp.GetRequiredService<HttpClient>(),
                        () => sp.GetRequiredService<LocalConfig>().ServerBaseUrl,
                        sp.GetRequiredService<IConfigStore>(),
                        sp.GetRequiredService<LocalConfig>(),
                        sp.GetRequiredService<ILogger<LicenseService>>()));

                services.AddSingleton<IToastService, ToastService>();

                // Migration DB MySQL : appliquée juste après extraction d'un build PROSERVE
                // si le ZIP contient un sous-répertoire _migrations/. La config DB et le
                // password (DPAPI) sont lus à la demande pour refléter les changements
                // utilisateur dans Settings sans recréer le service.
                services.AddSingleton<IDatabaseMigrationService>(sp =>
                    new DatabaseMigrationService(
                        configProvider: () => sp.GetRequiredService<LocalConfig>().Database,
                        passwordProvider: () => DatabasePasswordProtector.Unprotect(
                            sp.GetRequiredService<LocalConfig>().Database.EncryptedPassword),
                        sp.GetRequiredService<ILogger<DatabaseMigrationService>>()));

                // Déploiement de l'outil Report (page web XAMPP) à l'install d'une
                // nouvelle version : copie atomique de _report/ → C:\xampp\htdocs\ProserveReport\
                services.AddSingleton<IReportToolDeployer>(sp =>
                    new ReportToolDeployer(
                        configProvider: () => sp.GetRequiredService<LocalConfig>().ReportTool,
                        sp.GetRequiredService<ILogger<ReportToolDeployer>>()));

                // Déploiement de l'outil Documentation (parallèle à Report) :
                // copie atomique de _doc/ → C:\xampp\htdocs\ProserveDoc\
                services.AddSingleton<IDocToolDeployer>(sp =>
                    new DocToolDeployer(
                        configProvider: () => sp.GetRequiredService<LocalConfig>().DocTool,
                        sp.GetRequiredService<ILogger<DocToolDeployer>>()));

                services.AddTransient<SettingsViewModel>();
                services.AddSingleton<MainViewModel>();
                services.AddSingleton<MainWindow>();
            })
            .Build();

        var window = _host.Services.GetRequiredService<MainWindow>();
        window.DataContext = _host.Services.GetRequiredService<MainViewModel>();
        // Mise en page droite-à-gauche pour les langues RTL (arabe).
        if (Strings.IsRightToLeft)
            window.FlowDirection = FlowDirection.RightToLeft;
        MainWindow = window;
        window.Show();
    }

    protected override void OnExit(ExitEventArgs e)
    {
        Log.Information("PSLauncher shutting down");
        Log.CloseAndFlush();
        _host?.Dispose();
        base.OnExit(e);
    }
}
