using System.Windows;
using System.Windows.Controls;
using Microsoft.Web.WebView2.Core;
using Microsoft.Web.WebView2.Wpf;
using NexusShell.Models.Browser;
using NexusShell.ViewModels;

namespace NexusShell.Views;

public partial class NexusBrowserView : UserControl
{
    private readonly Dictionary<Guid, WebView2> _webViews = new();
    private NexusBrowserViewModel? _viewModel;
    private bool _isHooked;

    public NexusBrowserView()
    {
        InitializeComponent();
        DataContextChanged += OnDataContextChanged;
    }

    private void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
    {
        if (_viewModel is not null)
        {
            UnhookViewModel(_viewModel);
        }

        _viewModel = e.NewValue as NexusBrowserViewModel;
        if (_viewModel is null)
        {
            return;
        }

        HookViewModel(_viewModel);
        foreach (var tab in _viewModel.Tabs)
        {
            _ = EnsureWebViewAsync(tab);
        }

        UpdateVisibleWebView();
    }

    private void HookViewModel(NexusBrowserViewModel vm)
    {
        if (_isHooked)
        {
            return;
        }

        vm.NavigateRequested += OnNavigateRequested;
        vm.BackRequested += OnBackRequested;
        vm.ForwardRequested += OnForwardRequested;
        vm.RefreshRequested += OnRefreshRequested;
        vm.TabWebViewRequested += OnTabWebViewRequested;
        vm.PropertyChanged += OnViewModelPropertyChanged;
        _isHooked = true;
    }

    private void UnhookViewModel(NexusBrowserViewModel vm)
    {
        vm.NavigateRequested -= OnNavigateRequested;
        vm.BackRequested -= OnBackRequested;
        vm.ForwardRequested -= OnForwardRequested;
        vm.RefreshRequested -= OnRefreshRequested;
        vm.TabWebViewRequested -= OnTabWebViewRequested;
        vm.PropertyChanged -= OnViewModelPropertyChanged;
        _isHooked = false;
    }

    private void OnViewModelPropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
    {
        if (e.PropertyName == nameof(NexusBrowserViewModel.ActiveTab))
        {
            UpdateVisibleWebView();
        }
    }

    private void OnTabWebViewRequested(object? sender, BrowserTab tab) =>
        _ = EnsureWebViewAsync(tab);

    private async void OnNavigateRequested(object? sender, BrowserNavigationEventArgs e)
    {
        var webView = await EnsureWebViewAsync(_viewModel!.Tabs.First(t => t.Id == e.TabId));
        if (e.IsHome)
        {
            webView.CoreWebView2?.NavigateToString(_viewModel!.HomePageHtml);
            return;
        }

        webView.Source = new Uri(e.Url);
    }

    private async void OnBackRequested(object? sender, EventArgs e)
    {
        var webView = await GetActiveWebViewAsync();
        webView?.CoreWebView2?.GoBack();
    }

    private async void OnForwardRequested(object? sender, EventArgs e)
    {
        var webView = await GetActiveWebViewAsync();
        webView?.CoreWebView2?.GoForward();
    }

    private async void OnRefreshRequested(object? sender, EventArgs e)
    {
        var webView = await GetActiveWebViewAsync();
        if (webView?.CoreWebView2 is null)
        {
            return;
        }

        var tab = _viewModel?.ActiveTab;
        if (tab is not null &&
            (string.Equals(tab.CurrentUrl, NexusBrowserConstants.HomeUrl, StringComparison.OrdinalIgnoreCase) ||
             tab.CurrentUrl.StartsWith("about:", StringComparison.OrdinalIgnoreCase)))
        {
            webView.CoreWebView2.NavigateToString(_viewModel!.HomePageHtml);
            return;
        }

        webView.CoreWebView2.Reload();
    }

    private async Task<WebView2?> GetActiveWebViewAsync()
    {
        var tab = _viewModel?.ActiveTab;
        if (tab is null)
        {
            return null;
        }

        return await EnsureWebViewAsync(tab);
    }

    private async Task<WebView2> EnsureWebViewAsync(BrowserTab tab)
    {
        if (_webViews.TryGetValue(tab.Id, out var existing))
        {
            return existing;
        }

        var webView = new WebView2
        {
            DefaultBackgroundColor = System.Drawing.Color.FromArgb(255, 2, 7, 18),
            Visibility = tab.IsActive ? Visibility.Visible : Visibility.Collapsed,
        };

        _webViews[tab.Id] = webView;
        WebViewHost.Children.Add(webView);

        await webView.EnsureCoreWebView2Async();
        ConfigureWebView(webView, tab);

        if (string.Equals(tab.CurrentUrl, NexusBrowserConstants.HomeUrl, StringComparison.OrdinalIgnoreCase) ||
            tab.CurrentUrl.StartsWith("about:", StringComparison.OrdinalIgnoreCase))
        {
            webView.CoreWebView2!.NavigateToString(_viewModel!.HomePageHtml);
        }
        else if (Uri.TryCreate(tab.CurrentUrl, UriKind.Absolute, out var uri))
        {
            webView.Source = uri;
        }
        else
        {
            webView.CoreWebView2!.NavigateToString(_viewModel!.HomePageHtml);
        }

        return webView;
    }

    private void ConfigureWebView(WebView2 webView, BrowserTab tab)
    {
        var core = webView.CoreWebView2!;
        core.Settings.AreDefaultContextMenusEnabled = false;
        core.Settings.AreDevToolsEnabled = false;
        core.Settings.IsStatusBarEnabled = false;
        core.Settings.IsZoomControlEnabled = false;

        core.NavigationStarting += (_, args) =>
        {
            if (_viewModel is null)
            {
                return;
            }

            var uri = args.Uri;
            if (_viewModel is not null &&
                App.Services.BrowserService.NavigationService.IsBlocked(uri))
            {
                args.Cancel = true;
                tab.BlockedMessage = NexusBrowserConstants.BlockedMessage;
                return;
            }

            tab.BlockedMessage = null;
            tab.IsLoading = true;
            tab.CurrentUrl = uri;
            _viewModel?.UpdateFromWebView(tab.Id, uri, tab.Title, true, core.CanGoBack, core.CanGoForward);
        };

        core.NavigationCompleted += (_, args) =>
        {
            var url = core.Source ?? tab.CurrentUrl;
            var title = core.DocumentTitle;
            tab.IsLoading = false;
            _viewModel?.UpdateFromWebView(tab.Id, url, title, false, core.CanGoBack, core.CanGoForward);
        };

        core.DocumentTitleChanged += (_, _) =>
        {
            _viewModel?.UpdateFromWebView(
                tab.Id,
                core.Source ?? tab.CurrentUrl,
                core.DocumentTitle,
                tab.IsLoading,
                core.CanGoBack,
                core.CanGoForward);
        };

        core.NewWindowRequested += (_, args) =>
        {
            args.Handled = true;
            if (_viewModel is not null && !string.IsNullOrWhiteSpace(args.Uri))
            {
                _viewModel.NavigateInput(args.Uri);
            }
        };
    }

    private void UpdateVisibleWebView()
    {
        var activeId = _viewModel?.ActiveTab?.Id;
        foreach (var pair in _webViews)
        {
            pair.Value.Visibility = pair.Key == activeId ? Visibility.Visible : Visibility.Collapsed;
        }
    }
}
