using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Media;
using NexusShell.Models;
using NexusShell.Views;

namespace NexusShell.Services;

public sealed class ShellEnvironmentService
{
    private bool _taskbarsHidden;

    public void ApplyPrimaryWindow(Window window, NexusUserSettings settings, NexusPlatformLockService? platformLock = null)
    {
        var hideTaskbar = platformLock?.ShouldHideWindowsTaskbar(settings)
            ?? (settings.HideWindowsTaskbar && !SafePocRuntime.IsOverlayMode);
        UpdateTaskbarVisibility(hideTaskbar);

        var shellFullscreen = platformLock?.ShouldUseShellFullscreen(settings) ?? settings.ShellFullscreen;
        if (!shellFullscreen)
        {
            window.Topmost = false;
            window.WindowState = WindowState.Maximized;
            window.WindowStyle = WindowStyle.SingleBorderWindow;
            window.ResizeMode = ResizeMode.CanResize;
            return;
        }

        window.WindowStyle = WindowStyle.None;
        window.ResizeMode = ResizeMode.NoResize;
        window.Topmost = true;
        window.WindowState = WindowState.Normal;

        var dpi = VisualTreeHelper.GetDpi(window);
        if (settings.SpanAllMonitors)
        {
            PlaceWindow(
                window,
                SystemParameters.VirtualScreenLeft,
                SystemParameters.VirtualScreenTop,
                SystemParameters.VirtualScreenWidth,
                SystemParameters.VirtualScreenHeight);
            return;
        }

        var primary = MonitorLayoutService.GetPrimaryScreen(dpi.DpiScaleX, dpi.DpiScaleY);
        if (primary is null)
        {
            window.WindowState = WindowState.Maximized;
            return;
        }

        PlaceWindow(window, primary.Bounds);
    }

    public IReadOnlyList<MonitorDesktopWindow> SyncSecondaryMonitors(
        Window owner,
        NexusUserSettings settings,
        object desktopDataContext)
    {
        var windows = new List<MonitorDesktopWindow>();
        if (!settings.ShellFullscreen || settings.SpanAllMonitors)
        {
            return windows;
        }

        var dpi = VisualTreeHelper.GetDpi(owner);
        foreach (var screen in MonitorLayoutService.GetSecondaryScreens(dpi.DpiScaleX, dpi.DpiScaleY))
        {
            var window = new MonitorDesktopWindow
            {
                Owner = owner,
                DataContext = desktopDataContext,
                ShowInTaskbar = false,
                Topmost = true,
            };

            PlaceWindow(window, screen.Bounds);
            window.Show();
            windows.Add(window);
        }

        return windows;
    }

    public void Restore()
    {
        SetTaskbarVisibility(true);
        _taskbarsHidden = false;
    }

    private void UpdateTaskbarVisibility(bool hide)
    {
        if (hide)
        {
            HideTaskbars();
        }
        else
        {
            ShowTaskbars();
        }
    }

    private static void PlaceWindow(Window window, Rect bounds) =>
        PlaceWindow(window, bounds.X, bounds.Y, bounds.Width, bounds.Height);

    private static void PlaceWindow(Window window, double left, double top, double width, double height)
    {
        window.WindowStartupLocation = WindowStartupLocation.Manual;
        window.Left = left;
        window.Top = top;
        window.Width = width;
        window.Height = height;
    }

    private void HideTaskbars()
    {
        if (_taskbarsHidden)
        {
            return;
        }

        SetTaskbarVisibility(false);
        _taskbarsHidden = true;
    }

    private void ShowTaskbars()
    {
        if (!_taskbarsHidden)
        {
            return;
        }

        SetTaskbarVisibility(true);
        _taskbarsHidden = false;
    }

    private static void SetTaskbarVisibility(bool visible)
    {
        var command = visible ? SwShow : SwHide;
        ToggleWindow("Shell_TrayWnd", command);
        ToggleWindow("Shell_SecondaryTrayWnd", command);
    }

    private static void ToggleWindow(string className, int command)
    {
        var handle = FindWindow(className, null);
        while (handle != IntPtr.Zero)
        {
            ShowWindow(handle, command);
            handle = FindWindowEx(IntPtr.Zero, handle, className, null);
        }
    }

    private const int SwHide = 0;
    private const int SwShow = 5;

    [DllImport("user32.dll", CharSet = CharSet.Unicode)]
    private static extern IntPtr FindWindow(string? lpClassName, string? lpWindowName);

    [DllImport("user32.dll", CharSet = CharSet.Unicode)]
    private static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string? lpszClass, string? lpszWindow);

    [DllImport("user32.dll")]
    private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
}
