using System.Text;
using Microsoft.UI;
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.Web.WebView2.Core;
using Windows.Graphics;
using Windows.System;
using WinRT.Interop;
using YMhut.Box.Core.App;
using YMhut.Box.Core.Logging;
using YMhut.Box.Core.Settings;
using YMhut.Box.WinUI.Services;

namespace YMhut.Box.WinUI.Views;

public sealed class SafeBrowserPage : Page
{
    public const string HomeUrl = "https://www.bing.com";
    public const string BingSearchUrl = "https://www.bing.com/search?q=";
    private const int FlushVisitCount = 15;
    private const int FlushTextLength = 12000;

    private readonly ILogService _logService = AppServices.GetRequiredService<ILogService>();
    private readonly AppPaths _paths = AppServices.GetRequiredService<AppPaths>();
    private readonly IIndependentWindowHostLauncher _windowHostLauncher = AppServices.GetRequiredService<IIndependentWindowHostLauncher>();
    private readonly WebView2EnvironmentFactory _webViewEnvironmentFactory = AppServices.GetRequiredService<WebView2EnvironmentFactory>();
    private readonly string _sessionId = DateTimeOffset.Now.ToString("yyyyMMddHHmmss");
    private readonly string _userDataFolder;
    private readonly string _initialUrl;
    private readonly List<string> _pendingVisits = [];
    private readonly WebView2 _browser = new();
    private readonly ProgressBar _loadingBar = new()
    {
        Height = 3,
        IsIndeterminate = true,
        Visibility = Visibility.Collapsed,
        VerticalAlignment = VerticalAlignment.Top
    };

    private int _logPart = 1;
    private bool _loaded;
    private bool _flushing;
    private string _currentAddress = HomeUrl;

    public SafeBrowserPage(string userDataFolder, string? initialUrl = null)
    {
        _userDataFolder = userDataFolder;
        _initialUrl = NormalizeNavigationTarget(initialUrl, HomeUrl);
        _currentAddress = _initialUrl;
        Background = ModernUi.AppBackground;
        Content = BuildContent();
        Loaded += SafeBrowserPage_Loaded;
        Unloaded += SafeBrowserPage_Unloaded;
    }

    public event EventHandler? NavigationStateChanged;

    public bool CanGoBack => _browser.CanGoBack;

    public bool CanGoForward => _browser.CanGoForward;

    public bool IsLoading => _loadingBar.Visibility == Visibility.Visible;

    public string CurrentAddress => _browser.Source?.ToString() ?? _currentAddress;

    public void GoBackFromShell()
    {
        if (_browser.CanGoBack)
        {
            _browser.GoBack();
        }
    }

    public void GoForwardFromShell()
    {
        if (_browser.CanGoForward)
        {
            _browser.GoForward();
        }
    }

    public void RefreshOrStopFromShell()
    {
        if (IsLoading)
        {
            _browser.CoreWebView2?.Stop();
            return;
        }

        _browser.Reload();
    }

    public void NavigateHomeFromShell()
    {
        Navigate(HomeUrl);
    }

    public void NavigateFromShell(string raw)
    {
        Navigate(raw);
    }

    public async Task OpenCurrentInSystemBrowserAsync()
    {
        await Launcher.LaunchUriAsync(_browser.Source ?? new Uri(HomeUrl));
    }

    public async Task FlushAsync()
    {
        if (_flushing || _pendingVisits.Count == 0)
        {
            return;
        }

        _flushing = true;
        try
        {
            var detail = string.Join(Environment.NewLine, _pendingVisits);
            _pendingVisits.Clear();
            await _logService.WriteAsync(
                "Information",
                "safe_browser",
                $"安全浏览器访问记录 #{_logPart++}",
                $"会话：{_sessionId}{Environment.NewLine}{detail}");
        }
        finally
        {
            _flushing = false;
        }
    }

    private UIElement BuildContent()
    {
        var browserHost = new Grid
        {
            Background = ModernUi.AppBackground,
            BorderBrush = ModernUi.Stroke,
            BorderThickness = new Thickness(0, 1, 0, 0)
        };
        browserHost.Children.Add(_browser);
        browserHost.Children.Add(_loadingBar);
        return browserHost;
    }

    private async void SafeBrowserPage_Loaded(object sender, RoutedEventArgs e)
    {
        if (_loaded)
        {
            return;
        }

        _loaded = true;
        try
        {
            Directory.CreateDirectory(_userDataFolder);
            var environment = await _webViewEnvironmentFactory.CreateAsync(
                $"SafeBrowser\\{_sessionId}",
                userDataFolder: _userDataFolder);
            var controllerOptions = environment.CreateCoreWebView2ControllerOptions();
            controllerOptions.IsInPrivateModeEnabled = true;
            await _browser.EnsureCoreWebView2Async(environment, controllerOptions);
            _browser.CoreWebView2.Settings.IsStatusBarEnabled = false;
            _browser.CoreWebView2.Settings.IsZoomControlEnabled = true;
            _browser.CoreWebView2.Settings.AreDefaultContextMenusEnabled = true;
            _browser.CoreWebView2.Settings.AreBrowserAcceleratorKeysEnabled = true;
            _browser.CoreWebView2.Settings.IsGeneralAutofillEnabled = false;
            _browser.CoreWebView2.Settings.IsPasswordAutosaveEnabled = false;
            _browser.CoreWebView2.NewWindowRequested += Browser_NewWindowRequested;
            _browser.NavigationStarting += Browser_NavigationStarting;
            _browser.NavigationCompleted += Browser_NavigationCompleted;
            Navigate(_initialUrl);
            await _logService.WriteAsync("Information", "safe_browser", "安全浏览器会话已启动", $"会话：{_sessionId}");
        }
        catch (Exception exception)
        {
            CrashLog.Write(exception);
            await ShowWebViewErrorAsync(exception);
        }
    }

    private async void SafeBrowserPage_Unloaded(object sender, RoutedEventArgs e)
    {
        if (_browser.CoreWebView2 is not null)
        {
            _browser.CoreWebView2.NewWindowRequested -= Browser_NewWindowRequested;
        }

        await FlushAsync();
    }

    private async Task ShowWebViewErrorAsync(Exception exception)
    {
        var dialog = new ContentDialog
        {
            Title = AppLocalizer.T("WebView2 初始化失败", "WebView2 initialization failed"),
            Content = AppLocalizer.T(
                $"请确认 Microsoft Edge WebView2 Runtime 已安装。\n\n{AppLocalizer.SanitizeSensitiveText(exception.Message, 160)}",
                $"Please make sure Microsoft Edge WebView2 Runtime is installed.\n\n{AppLocalizer.SanitizeSensitiveText(exception.Message, 160)}"),
            CloseButtonText = AppLocalizer.T("知道了", "OK"),
            XamlRoot = XamlRoot
        };
        await dialog.ShowAsync();
    }

    private async void Browser_NewWindowRequested(CoreWebView2 sender, CoreWebView2NewWindowRequestedEventArgs args)
    {
        args.Handled = true;
        var target = NormalizePopupTarget(args.Uri);

        try
        {
            await _windowHostLauncher.LaunchAsync(IndependentWindowHostOptions.Browser(
                "Safe Browser",
                target,
                _paths));
            await _logService.WriteAsync(
                "Information",
                "safe_browser",
                "安全浏览器新窗口请求已接管",
                SafeUrl(target));
        }
        catch (Exception exception)
        {
            CrashLog.Write(exception);
            Navigate(target);
        }
    }

    private void Browser_NavigationStarting(WebView2 sender, CoreWebView2NavigationStartingEventArgs args)
    {
        _loadingBar.Visibility = Visibility.Visible;
        _currentAddress = args.Uri;
        NavigationStateChanged?.Invoke(this, EventArgs.Empty);
    }

    private void Browser_NavigationCompleted(WebView2 sender, CoreWebView2NavigationCompletedEventArgs args)
    {
        _loadingBar.Visibility = Visibility.Collapsed;
        var source = _browser.Source?.ToString() ?? _currentAddress;
        _currentAddress = source;
        RecordVisit(source, args.IsSuccess, args.WebErrorStatus.ToString());
        NavigationStateChanged?.Invoke(this, EventArgs.Empty);
    }

    private void Navigate(string raw)
    {
        var target = NormalizeNavigationTarget(raw, HomeUrl);
        _currentAddress = target;
        _browser.Source = new Uri(target);
        NavigationStateChanged?.Invoke(this, EventArgs.Empty);
    }

    private void RecordVisit(string rawUrl, bool success, string status)
    {
        var line = $"[{DateTime.Now:HH:mm:ss}] {(success ? "OK" : status)} {SafeUrl(rawUrl)}";
        _pendingVisits.Add(line);
        var length = _pendingVisits.Sum(item => item.Length);
        if (_pendingVisits.Count >= FlushVisitCount || length >= FlushTextLength)
        {
            _ = FlushAsync();
        }
    }

    private static string SafeUrl(string rawUrl)
    {
        if (!Uri.TryCreate(rawUrl, UriKind.Absolute, out var uri))
        {
            return rawUrl;
        }

        var builder = new StringBuilder();
        builder.Append(uri.Scheme).Append("://").Append(uri.Host);
        if (!uri.IsDefaultPort)
        {
            builder.Append(':').Append(uri.Port);
        }

        if (!string.IsNullOrWhiteSpace(uri.AbsolutePath) && uri.AbsolutePath != "/")
        {
            builder.Append(uri.AbsolutePath);
        }

        return builder.ToString();
    }

    public static string NormalizeNavigationTarget(string? raw, string fallback = HomeUrl)
    {
        var input = (raw ?? string.Empty).Trim();
        if (string.IsNullOrWhiteSpace(input))
        {
            return fallback;
        }

        if (!input.StartsWith("http://", StringComparison.OrdinalIgnoreCase) &&
            !input.StartsWith("https://", StringComparison.OrdinalIgnoreCase))
        {
            input = input.Contains('.') && !input.Contains(' ')
                ? $"https://{input}"
                : BingSearchUrl + Uri.EscapeDataString(input);
        }

        return Uri.TryCreate(input, UriKind.Absolute, out _) ? input : fallback;
    }

    private static string NormalizePopupTarget(string? raw)
    {
        var input = (raw ?? string.Empty).Trim();
        if (string.IsNullOrWhiteSpace(input) ||
            string.Equals(input, "about:blank", StringComparison.OrdinalIgnoreCase))
        {
            return HomeUrl;
        }

        return NormalizeNavigationTarget(input, HomeUrl);
    }
}

public sealed class SafeBrowserWindow : Window
{
    private readonly SafeBrowserPage _page;
    private readonly IndependentWindowShell _shell;
    private readonly TitleBarDragRegionHelper _titleBarHelper;
    private readonly WindowMovePerformanceHelper _movePerformanceHelper;
    private readonly ISettingsService _settingsService = AppServices.GetRequiredService<ISettingsService>();
    private readonly IUiPerformanceCoordinator _uiPerformanceCoordinator = AppServices.GetRequiredService<IUiPerformanceCoordinator>();
    private readonly string _userDataFolder;
    private IDisposable? _moveLightModeScope;

    public SafeBrowserWindow(string? initialUrl = null)
    {
        Title = AppLocalizer.T("YMhut Box · 安全浏览器", "YMhut Box · Safe Browser");
        var paths = AppServices.GetRequiredService<AppPaths>();
        _userDataFolder = Path.Combine(paths.Cache, "SafeBrowser", Guid.NewGuid().ToString("N"));
        _page = new SafeBrowserPage(_userDataFolder, initialUrl);
        _shell = new IndependentWindowShell(
            AppLocalizer.T("使用 Bing 搜索，或者输入网址", "Search with Bing or enter address"),
            "\uE774",
            string.Empty,
            IndependentWindowShellMode.Browser);
        _shell.SetButtonGlyph(_shell.ExtensionButton, "\uE8A7");
        _shell.SetButtonToolTip(_shell.ExtensionButton, AppLocalizer.T("使用系统浏览器打开", "Open in system browser"));
        _shell.SetButtonToolTip(_shell.MenuButton, AppLocalizer.T("菜单", "Menu"));
        _shell.BackButton.Click += (_, _) => _page.GoBackFromShell();
        _shell.ForwardButton.Click += (_, _) => _page.GoForwardFromShell();
        _shell.StopButton.Click += (_, _) => _page.RefreshOrStopFromShell();
        _shell.ExtensionButton.Click += async (_, _) => await _page.OpenCurrentInSystemBrowserAsync();
        _shell.CommandSubmitted += (_, text) => _page.NavigateFromShell(text);
        _shell.MenuButton.Flyout = BuildBrowserMenu();
        _shell.SetContent(_page);
        _page.NavigationStateChanged += Page_NavigationStateChanged;
        Content = _shell.Root;
        WindowIconService.ApplyAppIcon(this);
        _titleBarHelper = new TitleBarDragRegionHelper(this, _shell.TitleBar, _shell.PassthroughElements);
        _movePerformanceHelper = WindowMovePerformanceHelper.Attach(
            this,
            () => _settingsService.Current.WindowBackdrop,
            active =>
            {
                if (active)
                {
                    _moveLightModeScope ??= _uiPerformanceCoordinator.EnterLightMode("browser-window-move");
                }
                else
                {
                    _moveLightModeScope?.Dispose();
                    _moveLightModeScope = null;
                }
            });
        Closed += SafeBrowserWindow_Closed;
        ConfigureWindowSize();
        UpdateShellState();
    }

    private async void SafeBrowserWindow_Closed(object sender, WindowEventArgs args)
    {
        _moveLightModeScope?.Dispose();
        await _page.FlushAsync();
        try
        {
            if (Directory.Exists(_userDataFolder))
            {
                Directory.Delete(_userDataFolder, recursive: true);
            }
        }
        catch (Exception exception)
        {
            CrashLog.Write(exception);
        }
    }

    private void ConfigureWindowSize()
    {
        var hwnd = WindowNative.GetWindowHandle(this);
        var id = Win32Interop.GetWindowIdFromWindow(hwnd);
        var appWindow = AppWindow.GetFromWindowId(id);
        appWindow.Resize(new SizeInt32(1180, 760));
    }

    private MenuFlyout BuildBrowserMenu()
    {
        var flyout = new MenuFlyout();

        var home = new MenuFlyoutItem { Text = AppLocalizer.T("主页", "Home"), Icon = new SymbolIcon(Symbol.Home) };
        home.Click += (_, _) => _page.NavigateHomeFromShell();
        flyout.Items.Add(home);

        var refresh = new MenuFlyoutItem { Text = AppLocalizer.T("刷新", "Refresh"), Icon = new SymbolIcon(Symbol.Sync) };
        refresh.Click += (_, _) => _page.RefreshOrStopFromShell();
        flyout.Items.Add(refresh);

        var external = new MenuFlyoutItem { Text = AppLocalizer.T("使用系统浏览器打开", "Open in system browser"), Icon = new SymbolIcon(Symbol.OpenFile) };
        external.Click += async (_, _) => await _page.OpenCurrentInSystemBrowserAsync();
        flyout.Items.Add(external);

        return flyout;
    }

    private void Page_NavigationStateChanged(object? sender, EventArgs e)
    {
        DispatcherQueue.TryEnqueue(UpdateShellState);
    }

    private void UpdateShellState()
    {
        _shell.SetCommandText(_page.CurrentAddress);
        _shell.BackButton.IsEnabled = _page.CanGoBack;
        _shell.ForwardButton.IsEnabled = _page.CanGoForward;
        var loading = _page.IsLoading;
        _shell.SetButtonGlyph(_shell.StopButton, loading ? "\uE71A" : "\uE72C");
        _shell.SetButtonToolTip(_shell.StopButton, loading ? AppLocalizer.T("停止", "Stop") : AppLocalizer.T("刷新", "Refresh"));
    }
}
