using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Interop;
using Microsoft.Web.WebView2.Wpf;
using PSLauncher.App.ViewModels;
using PSLauncher.Core.Localization;

namespace PSLauncher.App.Views;

public partial class MainWindow : Window
{
    private static readonly int WM_PSLAUNCHER_BRING = RegisterWindowMessage("PSLAUNCHER_BRING_TO_FRONT");

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

    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool SetForegroundWindow(IntPtr hWnd);

    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

    private const int SW_RESTORE = 9;

    // ===== WM_GETMINMAXINFO : contraint la taille en mode Maximized à la work area =====
    // Avec WindowStyle="None" + WindowChrome, Windows maximise la fenêtre à la taille
    // PHYSIQUE du moniteur (incluant la zone de la taskbar), ce qui fait passer le footer
    // sous la barre des tâches → barre de DL invisible. Le fix canonique est de hooker
    // WM_GETMINMAXINFO et de remplacer ptMaxSize/ptMaxPosition par les coordonnées de
    // la work area (rcWork) du moniteur sur lequel la fenêtre se trouve. Multi-écran-safe
    // (gère même les taskbars sur écran secondaire) et insensible au DPI.
    private const int WM_GETMINMAXINFO = 0x0024;
    private const uint MONITOR_DEFAULTTONEAREST = 0x00000002;

    [DllImport("user32.dll")]
    private static extern IntPtr MonitorFromWindow(IntPtr handle, uint flags);

    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool GetMonitorInfo(IntPtr hMonitor, ref MONITORINFO lpmi);

    [StructLayout(LayoutKind.Sequential)]
    private struct POINT { public int X; public int Y; }

    [StructLayout(LayoutKind.Sequential)]
    private struct RECT { public int Left, Top, Right, Bottom; }

    [StructLayout(LayoutKind.Sequential)]
    private struct MINMAXINFO
    {
        public POINT ptReserved;
        public POINT ptMaxSize;
        public POINT ptMaxPosition;
        public POINT ptMinTrackSize;
        public POINT ptMaxTrackSize;
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct MONITORINFO
    {
        public int cbSize;
        public RECT rcMonitor;
        public RECT rcWork;
        public uint dwFlags;
    }

    private static void HandleGetMinMaxInfo(IntPtr hwnd, IntPtr lParam)
    {
        var monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
        if (monitor == IntPtr.Zero) return;

        var info = new MONITORINFO { cbSize = Marshal.SizeOf<MONITORINFO>() };
        if (!GetMonitorInfo(monitor, ref info)) return;

        var mmi = Marshal.PtrToStructure<MINMAXINFO>(lParam);
        var work = info.rcWork;
        var monitorRect = info.rcMonitor;
        mmi.ptMaxPosition.X = Math.Abs(work.Left - monitorRect.Left);
        mmi.ptMaxPosition.Y = Math.Abs(work.Top - monitorRect.Top);
        mmi.ptMaxSize.X = Math.Abs(work.Right - work.Left);
        mmi.ptMaxSize.Y = Math.Abs(work.Bottom - work.Top);
        Marshal.StructureToPtr(mmi, lParam, true);
    }

    public MainWindow()
    {
        InitializeComponent();
        SourceInitialized += MainWindow_SourceInitialized;
        Closing += MainWindow_Closing;

        // Force un reload de la WebView quand son onglet redevient visible
        // (Library → Report ou Library → Doc), pour que l'utilisateur voie
        // toujours les données les plus à jour côté serveur PHP. Sans ça,
        // WebView2 garde la page en cache de la première navigation et ne
        // refresh jamais. Au tout premier affichage CoreWebView2 n'est pas
        // encore initialisée — le binding Source fait alors la nav initiale,
        // les reloads suivants kicker uniquement sur les changements d'onglet.
        ReportWebView.IsVisibleChanged += OnWebViewBecameVisible;
        DocsWebView.IsVisibleChanged += OnWebViewBecameVisible;
    }

    private static void OnWebViewBecameVisible(object sender, DependencyPropertyChangedEventArgs e)
    {
        if (e.NewValue is true && sender is WebView2 wv && wv.CoreWebView2 is not null)
        {
            wv.CoreWebView2.Reload();
        }
    }

    /// <summary>
    /// Si un DL est en cours, demande confirmation avant de fermer le launcher.
    /// La progression est de toute façon préservée pour reprise au prochain lancement,
    /// mais on évite à l'utilisateur de cliquer ✕ par accident en plein milieu d'un 14 Go.
    /// </summary>
    private void MainWindow_Closing(object? sender, System.ComponentModel.CancelEventArgs e)
    {
        if (DataContext is MainViewModel vm && vm.HasActiveDownload)
        {
            var version = vm.ActiveDownloadVersion ?? "?";
            var result = ThemedMessageBox.Show(
                Strings.MsgQuitWhileDownloadingConfirm(version),
                Strings.MsgBoxQuit,
                MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No);
            if (result != MessageBoxResult.Yes) e.Cancel = true;
        }
    }

    /// <summary>
    /// Hook le pump de messages Windows pour réagir au broadcast envoyé par une
    /// seconde instance qui veut nous mettre au premier plan.
    /// </summary>
    private void MainWindow_SourceInitialized(object? sender, EventArgs e)
    {
        var helper = new WindowInteropHelper(this);
        var source = HwndSource.FromHwnd(helper.Handle);
        source?.AddHook(WndProc);
    }

    private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
    {
        if (msg == WM_PSLAUNCHER_BRING)
        {
            if (WindowState == WindowState.Minimized)
                WindowState = WindowState.Normal;
            else
                ShowWindow(hwnd, SW_RESTORE);
            Activate();
            SetForegroundWindow(hwnd);
            handled = true;
        }
        else if (msg == WM_GETMINMAXINFO)
        {
            HandleGetMinMaxInfo(hwnd, lParam);
            // On laisse handled=false : Windows continue son traitement avec le MINMAXINFO
            // qu'on vient de réécrire (clamp à la work area du moniteur).
        }
        return IntPtr.Zero;
    }

    /// <summary>
    /// Le bouton "..." ouvre son ContextMenu sur clic gauche.
    /// </summary>
    private void OnMoreMenuClick(object sender, RoutedEventArgs e)
    {
        if (sender is Button btn && btn.ContextMenu is { } menu)
        {
            menu.PlacementTarget = btn;
            menu.Placement = PlacementMode.Bottom;
            menu.IsOpen = true;
        }
    }

    private void OnMinimizeClick(object sender, RoutedEventArgs e)
    {
        WindowState = WindowState.Minimized;
    }

    private void OnMaxRestoreClick(object sender, RoutedEventArgs e)
    {
        WindowState = WindowState == WindowState.Maximized ? WindowState.Normal : WindowState.Maximized;
    }

    private void OnCloseClick(object sender, RoutedEventArgs e)
    {
        Close();
    }
}
