using Microsoft.UI;
using Microsoft.UI.Text;
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Automation;
using Microsoft.UI.Xaml.Controls;
using System.Runtime.InteropServices;
using Windows.Graphics;
using WinRT.Interop;
using YMhut.Box.Core.Logging;
using YMhut.Box.Core.Settings;

namespace YMhut.Box.WinUI.Services;

public sealed class WindowStateService(
    ISettingsService settingsService,
    ITrayService trayService,
    ILogService logService)
{
    private Window? _window;
    private AppWindow? _appWindow;
    private WindowId _windowId;
    private bool _allowClose;

    public void Attach(Window window)
    {
        _window = window;
        _appWindow = GetAppWindow(window);
        if (_appWindow is null)
        {
            return;
        }

        _windowId = Win32Interop.GetWindowIdFromWindow(WindowNative.GetWindowHandle(window));
        trayService.ExitRequested += TrayService_ExitRequested;
        _appWindow.Closing += AppWindow_Closing;
        _appWindow.Changed += AppWindow_Changed;
        _ = RestoreAsync();
    }

    private async void TrayService_ExitRequested(object? sender, EventArgs e)
    {
        await SaveAsync();
        await logService.WriteAsync("Information", "window", "托盘菜单请求退出", "source=tray");
        _allowClose = true;
        _window?.Close();
    }

    private async Task RestoreAsync()
    {
        try
        {
            var settings = await settingsService.LoadAsync();
            if (_appWindow is null)
            {
                return;
            }

            if (!settings.RestoreWindowPosition || settings.WindowWidth is null || settings.WindowHeight is null)
            {
                EnsureVisibleOnCurrentDisplay();
                return;
            }

            var x = (int)Math.Round(settings.WindowX ?? 80);
            var y = (int)Math.Round(settings.WindowY ?? 80);
            var width = Math.Max(960, (int)Math.Round(settings.WindowWidth.Value));
            var height = Math.Max(640, (int)Math.Round(settings.WindowHeight.Value));
            var bounds = new RectInt32(x, y, width, height);
            if (!IsUsableBounds(bounds) || !IntersectsAnyDisplay(bounds))
            {
                bounds = DefaultWindowBounds(width, height);
                await logService.WriteAsync("Warning", "window", "忽略不可见窗口坐标，已恢复到当前屏幕", $"{x},{y},{width},{height}");
            }

            _appWindow.MoveAndResize(bounds);
            _appWindow.Show();
            if (settings.WindowMaximized && _appWindow.Presenter is OverlappedPresenter presenter)
            {
                presenter.Maximize();
            }

            _window?.Activate();
        }
        catch (Exception exception)
        {
            await logService.WriteAsync("Warning", "window", "窗口状态恢复失败", exception.Message);
            EnsureVisibleOnCurrentDisplay();
        }
    }

    private async void AppWindow_Changed(AppWindow sender, AppWindowChangedEventArgs args)
    {
        if (args.DidPositionChange || args.DidSizeChange || args.DidPresenterChange)
        {
            await SaveAsync();
        }
    }

    private async void AppWindow_Closing(AppWindow sender, AppWindowClosingEventArgs args)
    {
        if (_allowClose)
        {
            return;
        }

        var behavior = NormalizeCloseBehavior(settingsService.Current.CloseBehavior);
        if (behavior == "exit_directly")
        {
            await SaveAsync();
            await logService.WriteAsync("Information", "window", "关闭按钮直接退出", "behavior=exit_directly");
            _allowClose = true;
            return;
        }

        args.Cancel = true;
        await SaveAsync();
        await logService.WriteAsync("Information", "window", "关闭按钮请求已拦截", $"behavior={behavior}");
        if (behavior == "minimize_to_tray")
        {
            if (trayService.IsAvailable)
            {
                trayService.HideMainWindow();
                await logService.WriteAsync("Information", "window", "关闭按钮最小化到托盘", "behavior=minimize_to_tray");
            }
            else
            {
                ToastService.Show(AppLocalizer.T("托盘不可用，已改为直接退出。", "Tray is unavailable, exiting instead."), ToastKind.Warning);
                await logService.WriteAsync("Warning", "window", "托盘不可用，关闭行为降级为退出", "behavior=minimize_to_tray");
                _allowClose = true;
                _window?.Close();
            }

            return;
        }

        await ShowCloseDialogAsync();
    }

    private static string NormalizeCloseBehavior(string? behavior)
    {
        return (behavior ?? string.Empty).Trim().ToLowerInvariant() switch
        {
            "exit" or "exit_directly" or "direct_exit" => "exit_directly",
            "minimize" or "minimize_to_tray" or "minimize_then_exit" => "minimize_to_tray",
            _ => "ask"
        };
    }

    private async Task ShowCloseDialogAsync()
    {
        FrameworkElement? root;
        try
        {
            root = _window?.Content as FrameworkElement;
        }
        catch (COMException)
        {
            _allowClose = true;
            return;
        }

        if (root?.XamlRoot is null)
        {
            _allowClose = true;
            _window?.Close();
            return;
        }

        var rememberChoice = new CheckBox
        {
            Content = AppLocalizer.T("记住我的选择，以后不再询问", "Remember my choice and do not ask again"),
            Margin = new Thickness(0, 2, 0, 0)
        };
        AutomationProperties.SetName(
            rememberChoice,
            AppLocalizer.T("记住我的关闭选择，以后不再询问", "Remember my close choice and do not ask again"));

        var dialog = new ContentDialog
        {
            Title = AppLocalizer.T("关闭 YMhut Box", "Close YMhut Box"),
            Content = BuildCloseDialogContent(rememberChoice, trayService.IsAvailable),
            PrimaryButtonText = AppLocalizer.T("最小化到托盘", "Minimize to tray"),
            SecondaryButtonText = AppLocalizer.T("直接退出", "Exit directly"),
            CloseButtonText = AppLocalizer.T("取消", "Cancel"),
            IsPrimaryButtonEnabled = trayService.IsAvailable,
            DefaultButton = ContentDialogButton.Close,
            XamlRoot = root.XamlRoot
        };
        ContentDialogResult result;
        try
        {
            await logService.WriteAsync("Information", "window", "显示关闭行为询问弹窗", "behavior=ask");
            result = await dialog.ShowAsync();
        }
        catch (COMException)
        {
            await logService.WriteAsync("Warning", "window", "关闭询问弹窗显示失败，降级退出", "dialog=com_exception");
            _allowClose = true;
            return;
        }

        var remember = rememberChoice.IsChecked == true;
        if (result == ContentDialogResult.Primary)
        {
            if (trayService.IsAvailable)
            {
                await RememberCloseChoiceAsync("minimize_to_tray", remember);
                trayService.HideMainWindow();
                await logService.WriteAsync("Information", "window", "关闭询问选择最小化到托盘", $"result=primary; remembered={remember}");
            }
            else
            {
                ToastService.Show(AppLocalizer.T("托盘不可用，已改为直接退出。", "Tray is unavailable, exiting instead."), ToastKind.Warning);
                await logService.WriteAsync("Warning", "window", "关闭询问选择托盘但托盘不可用，降级退出", $"result=primary; remembered={remember}");
                _allowClose = true;
                _window?.Close();
            }
        }
        else if (result == ContentDialogResult.Secondary)
        {
            await RememberCloseChoiceAsync("exit_directly", remember);
            await logService.WriteAsync("Information", "window", "关闭询问选择直接退出", $"result=secondary; remembered={remember}");
            _allowClose = true;
            _window?.Close();
        }
        else
        {
            await logService.WriteAsync("Information", "window", "关闭询问已取消", "result=cancel");
        }
    }

    private async Task RememberCloseChoiceAsync(string behavior, bool remember)
    {
        if (!remember)
        {
            return;
        }

        await settingsService.UpdateAsync(settings => settings.CloseBehavior = behavior);
        ToastService.Show(AppLocalizer.T("关闭行为已保存。", "Close behavior saved."), ToastKind.Success);
    }

    private static UIElement BuildCloseDialogContent(CheckBox rememberChoice, bool trayAvailable)
    {
        var panel = new StackPanel
        {
            Spacing = 14,
            MinWidth = 360,
            MaxWidth = 460
        };

        panel.Children.Add(new TextBlock
        {
            Text = AppLocalizer.T(
                "请选择点击关闭按钮后的处理方式。只在本次生效；勾选下方选项后会自动保存为默认关闭行为。",
                "Choose what happens when you press the close button. The choice applies once unless you remember it below."),
            TextWrapping = TextWrapping.Wrap,
            LineHeight = 20
        });

        var options = new StackPanel { Spacing = 10 };
        options.Children.Add(BuildCloseOptionRow(
            "\uE75D",
            AppLocalizer.T("最小化到托盘", "Minimize to tray"),
            trayAvailable
                ? AppLocalizer.T("窗口隐藏到系统托盘，任务继续保持运行。", "Hide the window in the system tray while tasks keep running.")
                : AppLocalizer.T("当前环境未提供托盘图标，请选择直接退出。", "The tray icon is unavailable in this environment. Choose exit directly."),
            trayAvailable));
        options.Children.Add(BuildCloseOptionRow(
            "\uE8BB",
            AppLocalizer.T("直接退出", "Exit directly"),
            AppLocalizer.T("保存窗口位置后关闭应用。下载、工具执行和后台服务会一起结束。", "Save the window position and close the app. Downloads, tools, and background services will stop."),
            enabled: true));
        panel.Children.Add(options);
        panel.Children.Add(rememberChoice);

        return panel;
    }

    private static UIElement BuildCloseOptionRow(string glyph, string title, string description, bool enabled)
    {
        var row = new Grid
        {
            ColumnSpacing = 12,
            Opacity = enabled ? 1 : 0.58,
            MinHeight = 52
        };
        row.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
        row.ColumnDefinitions.Add(new ColumnDefinition());

        row.Children.Add(new FontIcon
        {
            Glyph = glyph,
            FontSize = 18,
            Width = 24,
            Height = 24,
            VerticalAlignment = VerticalAlignment.Top,
            Margin = new Thickness(0, 2, 0, 0)
        });

        var text = new StackPanel
        {
            Spacing = 3,
            Children =
            {
                new TextBlock
                {
                    Text = title,
                    FontWeight = FontWeights.SemiBold,
                    TextWrapping = TextWrapping.Wrap
                },
                new TextBlock
                {
                    Text = description,
                    TextWrapping = TextWrapping.Wrap,
                    LineHeight = 19,
                    Foreground = YMhut.Box.WinUI.ModernUi.TextSecondary
                }
            }
        };
        Grid.SetColumn(text, 1);
        row.Children.Add(text);

        return row;
    }

    private async Task SaveAsync()
    {
        if (_appWindow is null)
        {
            return;
        }

        var maximized = _appWindow.Presenter is OverlappedPresenter presenter && presenter.State == OverlappedPresenterState.Maximized;
        if (_appWindow.Presenter is OverlappedPresenter statePresenter &&
            statePresenter.State == OverlappedPresenterState.Minimized)
        {
            return;
        }

        var bounds = new RectInt32(
            _appWindow.Position.X,
            _appWindow.Position.Y,
            _appWindow.Size.Width,
            _appWindow.Size.Height);

        if (!_appWindow.IsVisible || !IsUsableBounds(bounds) || !IntersectsAnyDisplay(bounds))
        {
            return;
        }

        await settingsService.SaveWindowBoundsAsync(
            _appWindow.Position.X,
            _appWindow.Position.Y,
            _appWindow.Size.Width,
            _appWindow.Size.Height,
            maximized);
    }

    private static AppWindow? GetAppWindow(Window window)
    {
        var hwnd = WindowNative.GetWindowHandle(window);
        var windowId = Win32Interop.GetWindowIdFromWindow(hwnd);
        return AppWindow.GetFromWindowId(windowId);
    }

    private bool IntersectsAnyDisplay(RectInt32 bounds)
    {
        try
        {
            return DisplayArea.FindAll().Any(display => Intersects(bounds, display.WorkArea));
        }
        catch
        {
            return bounds.X > -10000 && bounds.Y > -10000;
        }
    }

    private RectInt32 DefaultWindowBounds(int requestedWidth = 1180, int requestedHeight = 760)
    {
        var width = Math.Clamp(requestedWidth, 960, 1440);
        var height = Math.Clamp(requestedHeight, 640, 960);
        try
        {
            var display = DisplayArea.GetFromWindowId(_windowId, DisplayAreaFallback.Primary);
            width = Math.Min(width, Math.Max(960, display.WorkArea.Width - 120));
            height = Math.Min(height, Math.Max(640, display.WorkArea.Height - 120));
            return new RectInt32(
                display.WorkArea.X + Math.Max(0, (display.WorkArea.Width - width) / 2),
                display.WorkArea.Y + Math.Max(0, (display.WorkArea.Height - height) / 2),
                width,
                height);
        }
        catch
        {
            return new RectInt32(80, 80, width, height);
        }
    }

    private void EnsureVisibleOnCurrentDisplay()
    {
        if (_appWindow is null)
        {
            return;
        }

        var bounds = new RectInt32(_appWindow.Position.X, _appWindow.Position.Y, _appWindow.Size.Width, _appWindow.Size.Height);
        if (!IsUsableBounds(bounds) || !IntersectsAnyDisplay(bounds))
        {
            _appWindow.MoveAndResize(DefaultWindowBounds());
        }

        _appWindow.Show();
        _window?.Activate();
    }

    private static bool IsUsableBounds(RectInt32 bounds)
    {
        return bounds.X > -30000
            && bounds.Y > -30000
            && bounds.Width >= 640
            && bounds.Height >= 420;
    }

    private static bool Intersects(RectInt32 left, RectInt32 right)
    {
        return left.X < right.X + right.Width
            && left.X + left.Width > right.X
            && left.Y < right.Y + right.Height
            && left.Y + left.Height > right.Y;
    }
}
