using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Input;
using NexusShell.Models.Browser;
using NexusShell.Services;

namespace NexusShell.ViewModels;

public sealed class NexusBrowserViewModel : INotifyPropertyChanged
{
    private readonly NexusBrowserService _browser;
    private string _addressBarText = string.Empty;
    private string _statusMessage = string.Empty;
    private bool _isFavorite;
    private bool _showMenu;
    private bool _showDownloadsMessage;

    public NexusBrowserViewModel(NexusBrowserService browser)
    {
        _browser = browser;
        Tabs = new ObservableCollection<BrowserTab>();

        _browser.TabService.TabsChanged += (_, _) => SyncTabs();
        _browser.TabService.ActiveTabChanged += (_, tab) => OnActiveTabChanged(tab);

        BackCommand = new RelayCommand(_ => RequestBack(), _ => ActiveTab?.CanGoBack == true);
        ForwardCommand = new RelayCommand(_ => RequestForward(), _ => ActiveTab?.CanGoForward == true);
        RefreshCommand = new RelayCommand(_ => RequestRefresh());
        HomeCommand = new RelayCommand(_ => NavigateInput(NexusBrowserConstants.HomeUrl));
        GoCommand = new RelayCommand(_ => NavigateInput(AddressBarText));
        NewTabCommand = new RelayCommand(_ => CreateTab());
        CloseTabCommand = new RelayCommand(p => CloseTab(p as BrowserTab));
        SelectTabCommand = new RelayCommand(p => SelectTab(p as BrowserTab));
        ToggleFavoriteCommand = new RelayCommand(_ => ToggleFavorite());
        DownloadsCommand = new RelayCommand(_ => ShowDownloadsMessage = true);
        MenuCommand = new RelayCommand(_ => ShowMenu = !ShowMenu);
        CloseCommand = new RelayCommand(_ => CloseRequested?.Invoke(this, EventArgs.Empty));
        DismissMessageCommand = new RelayCommand(_ =>
        {
            ShowDownloadsMessage = false;
            StatusMessage = string.Empty;
        });

        CreateTab();
    }

    public event PropertyChangedEventHandler? PropertyChanged;
    public event EventHandler? CloseRequested;
    public event EventHandler<BrowserNavigationEventArgs>? NavigateRequested;
    public event EventHandler? BackRequested;
    public event EventHandler? ForwardRequested;
    public event EventHandler? RefreshRequested;
    public event EventHandler<BrowserTab>? TabWebViewRequested;

    public ObservableCollection<BrowserTab> Tabs { get; }

    public BrowserTab? ActiveTab => _browser.TabService.ActiveTab;

    public string AddressBarText
    {
        get => _addressBarText;
        set { _addressBarText = value; OnPropertyChanged(); }
    }

    public string StatusMessage
    {
        get => _statusMessage;
        set { _statusMessage = value; OnPropertyChanged(); }
    }

    public bool IsFavorite
    {
        get => _isFavorite;
        set { _isFavorite = value; OnPropertyChanged(); }
    }

    public bool ShowMenu
    {
        get => _showMenu;
        set { _showMenu = value; OnPropertyChanged(); }
    }

    public bool ShowDownloadsMessage
    {
        get => _showDownloadsMessage;
        set
        {
            _showDownloadsMessage = value;
            OnPropertyChanged();
            if (value)
            {
                StatusMessage = NexusBrowserConstants.DownloadsPlaceholder;
            }
        }
    }

    public string SecurityLabel => ActiveTab?.SecurityLabel ?? "Lokal";

    public BrowserSecurityState SecurityState =>
        ActiveTab?.IsLoading == true
            ? BrowserSecurityState.Loading
            : ActiveTab?.SecurityState ?? BrowserSecurityState.Local;

    public bool HasBlockedMessage => ActiveTab?.HasBlockedMessage == true;
    public string? BlockedMessage => ActiveTab?.BlockedMessage;

    public ICommand BackCommand { get; }
    public ICommand ForwardCommand { get; }
    public ICommand RefreshCommand { get; }
    public ICommand HomeCommand { get; }
    public ICommand GoCommand { get; }
    public ICommand NewTabCommand { get; }
    public ICommand CloseTabCommand { get; }
    public ICommand SelectTabCommand { get; }
    public ICommand ToggleFavoriteCommand { get; }
    public ICommand DownloadsCommand { get; }
    public ICommand MenuCommand { get; }
    public ICommand CloseCommand { get; }
    public ICommand DismissMessageCommand { get; }

    public string HomePageHtml => _browser.GetHomePageHtml();

    public void OpenUrl(string? url)
    {
        if (string.IsNullOrWhiteSpace(url))
        {
            return;
        }

        NavigateInput(url);
    }

    public void CreateTab(string? initialUrl = null)
    {
        var tab = _browser.TabService.CreateTab(initialUrl);
        TabWebViewRequested?.Invoke(this, tab);
        SyncTabs();
        UpdateChromeFromActiveTab();
    }

    public void CloseTab(BrowserTab? tab)
    {
        if (tab is null)
        {
            return;
        }

        if (!_browser.TabService.CloseTab(tab))
        {
            return;
        }

        SyncTabs();
        UpdateChromeFromActiveTab();
    }

    public void SelectTab(BrowserTab? tab)
    {
        if (tab is null)
        {
            return;
        }

        _browser.TabService.SelectTab(tab);
        UpdateChromeFromActiveTab();
    }

    public void NavigateInput(string input)
    {
        var tab = ActiveTab;
        if (tab is null)
        {
            return;
        }

        var result = _browser.NavigationService.ResolveInput(input);
        if (result.IsBlocked)
        {
            tab.BlockedMessage = result.BlockMessage;
            tab.AddressText = input;
            AddressBarText = input;
            OnPropertyChanged(nameof(HasBlockedMessage));
            OnPropertyChanged(nameof(BlockedMessage));
            return;
        }

        tab.BlockedMessage = null;
        OnPropertyChanged(nameof(HasBlockedMessage));
        OnPropertyChanged(nameof(BlockedMessage));

        NavigateRequested?.Invoke(this, new BrowserNavigationEventArgs(tab.Id, result.Url, result.IsHome));
        tab.AddressText = result.IsHome ? string.Empty : input.Trim();
        AddressBarText = tab.AddressText;
    }

    public void UpdateFromWebView(
        Guid tabId,
        string url,
        string title,
        bool isLoading,
        bool canGoBack,
        bool canGoForward)
    {
        var tab = _browser.TabService.FindTab(tabId);
        if (tab is null)
        {
            return;
        }

        tab.CurrentUrl = url;
        tab.IsLoading = isLoading;
        tab.CanGoBack = canGoBack;
        tab.CanGoForward = canGoForward;

        if (!isLoading && !string.IsNullOrWhiteSpace(title))
        {
            tab.Title = title.Length > 28 ? $"{title[..25]}..." : title;
            _browser.NavigationService.RecordVisit(url, title);
        }

        tab.SecurityState = _browser.NavigationService.GetSecurityState(url, isLoading);

        if (tab.IsActive)
        {
            UpdateChromeFromActiveTab();
        }
    }

    private void RequestBack() => BackRequested?.Invoke(this, EventArgs.Empty);

    private void RequestForward() => ForwardRequested?.Invoke(this, EventArgs.Empty);

    private void RequestRefresh() => RefreshRequested?.Invoke(this, EventArgs.Empty);

    private void ToggleFavorite()
    {
        var tab = ActiveTab;
        if (tab is null || string.IsNullOrWhiteSpace(tab.CurrentUrl) ||
            tab.CurrentUrl.StartsWith("nexus:", StringComparison.OrdinalIgnoreCase) ||
            tab.CurrentUrl.StartsWith("about:", StringComparison.OrdinalIgnoreCase) ||
            tab.CurrentUrl.StartsWith("data:", StringComparison.OrdinalIgnoreCase))
        {
            return;
        }

        IsFavorite = _browser.ToggleFavorite(tab.Title, tab.CurrentUrl);
    }

    private void SyncTabs()
    {
        Tabs.Clear();
        foreach (var tab in _browser.TabService.Tabs)
        {
            Tabs.Add(tab);
        }

        OnPropertyChanged(nameof(ActiveTab));
    }

    private void OnActiveTabChanged(BrowserTab tab)
    {
        OnPropertyChanged(nameof(ActiveTab));
        UpdateChromeFromActiveTab();
    }

    private void UpdateChromeFromActiveTab()
    {
        var tab = ActiveTab;
        if (tab is null)
        {
            return;
        }

        AddressBarText = tab.AddressText;
        IsFavorite = _browser.IsFavorite(tab.CurrentUrl);
        OnPropertyChanged(nameof(SecurityLabel));
        OnPropertyChanged(nameof(SecurityState));
        OnPropertyChanged(nameof(HasBlockedMessage));
        OnPropertyChanged(nameof(BlockedMessage));
        CommandManager.InvalidateRequerySuggested();
    }

    private void OnPropertyChanged([CallerMemberName] string? name = null) =>
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}

public sealed class BrowserNavigationEventArgs : EventArgs
{
    public BrowserNavigationEventArgs(Guid tabId, string url, bool isHome)
    {
        TabId = tabId;
        Url = url;
        IsHome = isHome;
    }

    public Guid TabId { get; }
    public string Url { get; }
    public bool IsHome { get; }
}
