using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.WinUI;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Navigation;
using Windows.Foundation;
using Windows.System;
using Wino.Core.Domain;
using Wino.Core.Domain.Entities.Mail;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Domain.MenuItems;
using Wino.Core.Domain.Models;
using Wino.Core.Domain.Models.Calendar;
using Wino.Core.Domain.Models.Folders;
using Wino.Core.Domain.Models.MailItem;
using Wino.Core.Domain.Models.Navigation;
using Wino.Calendar.ViewModels;
using Wino.Mail.ViewModels;
using Wino.Mail.ViewModels.Data;
using Wino.Mail.WinUI.ViewModels;
using Wino.Mail.WinUI.Controls;
using Wino.Mail.WinUI.Helpers;
using Wino.MenuFlyouts;
using Wino.MenuFlyouts.Context;
using Wino.Messaging.Client.Accounts;
using Wino.Messaging.Client.Calendar;
using Wino.Messaging.Client.Mails;
using Wino.Messaging.Client.Shell;
using Wino.Messaging.UI;
using Wino.Views;

namespace Wino.Mail.WinUI.Views;

public sealed partial class WinoAppShell : Views.Abstract.WinoAppShellAbstract,
    IShellHost,
    IRecipient<AccountMenuItemExtended>,
    IRecipient<NavigateMailFolderEvent>,
    IRecipient<CreateNewMailWithMultipleAccountsRequested>,
    IRecipient<CalendarDisplayTypeChangedMessage>,
    IRecipient<AccountCreatedMessage>
{
    private const string StateDefaultShellContent = "DefaultShellContentState";
    private const string StateEventDetailsContent = "EventDetailsContentState";
    private WinoApplicationMode? _activeMode;
    private bool _isSyncingNavigationViewSelection;
    private bool _isSynchronizingVisibleDateRangeCalendar;
    private bool _isPreparedForWindowClose;

    public WinoAppShell()
    {
        InitializeComponent();

        var pageDispatcher = new WinUIDispatcher(DispatcherQueue);
        ViewModel.MailClient.Dispatcher = pageDispatcher;
        ViewModel.CalendarClient.Dispatcher = pageDispatcher;
        ViewModel.GetClient(WinoApplicationMode.Contacts).Dispatcher = pageDispatcher;
        ViewModel.GetClient(WinoApplicationMode.Settings).Dispatcher = pageDispatcher;

        ViewModel.MailClient.PropertyChanged += MailClientPropertyChanged;
        ViewModel.CalendarClient.PropertyChanged += CalendarClientPropertyChanged;
        ViewModel.PropertyChanged += ViewModelPropertyChanged;
        ViewModel.PreferencesService.PreferenceChanged += PreferencesServiceChanged;
        ViewModel.StatePersistenceService.StatePropertyChanged += StatePersistenceServiceChanged;

        InitializeCalendarControls();
        UpdateEventDetailsVisualState();
    }

    public bool HasShellContent => InnerShellFrame.Content != null;

    public Frame GetShellFrame() => InnerShellFrame;

    public void ActivateMode(WinoApplicationMode mode, ShellModeActivationContext activationContext)
    {
        if (_activeMode == mode && InnerShellFrame.Content != null)
            return;

        DeactivateCurrentMode();
        ResetShellModeNavigationState();

        _activeMode = mode;
        ViewModel.SetCurrentMode(mode);

        RefreshNavigationViewBindings(syncMailSelection: mode != WinoApplicationMode.Mail);

        ApplyModeLayout();
        UpdateTitleBarSubtitle();

        ViewModel.CurrentClient.Activate(activationContext);
        ResetShellModeNavigationState();
        NotifyTitleBarContentChanged();
    }

    protected override void OnNavigatedFrom(NavigationEventArgs e)
    {
        base.OnNavigatedFrom(e);

        if (!_isPreparedForWindowClose)
        {
            DeactivateCurrentMode();
            DetachLifetimeSubscriptions();
        }

        Bindings.StopTracking();
    }

    public void PrepareForWindowClose()
    {
        if (_isPreparedForWindowClose)
            return;

        _isPreparedForWindowClose = true;

        DeactivateAllShellClients();
        WeakReferenceMessenger.Default.Unregister<LanguageChanged>(this);
        UnregisterRecipients();
        DetachLifetimeSubscriptions();
        Bindings.StopTracking();

        navigationView.MenuItemsSource = null;
        CalendarHostListView.ItemsSource = null;
        WindowCleanupHelper.CleanupFrame(InnerShellFrame);
    }

    private void OnLoaded(object sender, RoutedEventArgs e)
    {
        UpdateNavigationPaneLayout(navigationView.DisplayMode);
        RefreshNavigationViewBindings();
        RefreshCalendarControls();

        if (_activeMode == null)
        {
            ActivateMode(ViewModel.StatePersistenceService.ApplicationMode, new ShellModeActivationContext
            {
                IsInitialActivation = true
            });
        }
    }

    private void ApplyModeLayout()
    {
        RefreshCalendarControls();
        UpdateEventDetailsVisualState();
        UpdateTitleBarSubtitle();
        UpdateNavigationPaneLayout(navigationView.DisplayMode);
        NotifyTitleBarContentChanged();
    }

    private void DeactivateCurrentMode()
    {
        if (_activeMode == WinoApplicationMode.Mail)
        {
            WeakReferenceMessenger.Default.Send(new ClearMailSelectionsRequested());
            WeakReferenceMessenger.Default.Send(new DisposeRenderingFrameRequested());
            ViewModel.StatePersistenceService.IsReadingMail = false;
            ViewModel.MailClient.Deactivate();
        }
        else if (_activeMode == WinoApplicationMode.Calendar)
        {
            ViewModel.StatePersistenceService.IsEventDetailsVisible = false;
            ViewModel.CalendarClient.Deactivate();
            WindowCleanupHelper.CleanupFrame(InnerShellFrame);
            GC.Collect();
        }
        else if (_activeMode == WinoApplicationMode.Contacts)
        {
            ViewModel.CurrentClient.Deactivate();
        }
        else if (_activeMode == WinoApplicationMode.Settings)
        {
            if (InnerShellFrame.Content is SettingsPage settingsPage)
            {
                settingsPage.ResetForModeSwitch();
            }

            ViewModel.CurrentClient.Deactivate();
        }
    }

    private void DeactivateAllShellClients()
    {
        ViewModel.StatePersistenceService.IsReadingMail = false;
        ViewModel.StatePersistenceService.IsEventDetailsVisible = false;

        ViewModel.MailClient.Deactivate();
        ViewModel.CalendarClient.Deactivate();

        if (ViewModel.GetClient(WinoApplicationMode.Contacts) is ContactsShellClient contactsClient)
        {
            contactsClient.PrepareForShellShutdown();
        }

        if (ViewModel.GetClient(WinoApplicationMode.Settings) is SettingsShellClient settingsClient)
        {
            settingsClient.PrepareForShellShutdown();
        }

        if (ViewModel.MailClient is MailAppShellViewModel mailClient)
        {
            mailClient.PrepareForShellShutdown();
        }

        if (ViewModel.CalendarClient is CalendarAppShellViewModel calendarClient)
        {
            calendarClient.PrepareForShellShutdown();
        }
    }

    private void DetachLifetimeSubscriptions()
    {
        ViewModel.MailClient.PropertyChanged -= MailClientPropertyChanged;
        ViewModel.CalendarClient.PropertyChanged -= CalendarClientPropertyChanged;
        ViewModel.PropertyChanged -= ViewModelPropertyChanged;
        ViewModel.PreferencesService.PreferenceChanged -= PreferencesServiceChanged;
        ViewModel.StatePersistenceService.StatePropertyChanged -= StatePersistenceServiceChanged;
    }

    private void ResetShellModeNavigationState()
    {
        InnerShellFrame.BackStack.Clear();
        InnerShellFrame.ForwardStack.Clear();
    }

    private void UpdateTitleBarSubtitle()
    {
        if (ViewModel.IsContactsMode)
        {
            ViewModel.StatePersistenceService.CoreWindowTitle = string.Empty;
            return;
        }

        if (ViewModel.IsCalendarMode)
        {
            ViewModel.StatePersistenceService.CoreWindowTitle = string.Empty;
            return;
        }

        ViewModel.StatePersistenceService.CoreWindowTitle = string.Empty;
    }

    private void InitializeCalendarControls()
    {
        CalendarHostListView.ItemsSource = ViewModel.CalendarClient.GroupedAccountCalendars;

        RefreshCalendarControls();
    }

    private void RefreshCalendarControls()
    {
        CalendarHostListView.ItemsSource = ViewModel.CalendarClient.GroupedAccountCalendars;
        SynchronizeVisibleDateRangeCalendar();
    }

    private void RefreshNavigationViewBindings(bool syncMailSelection = true)
    {
        navigationView.MenuItemsSource = ViewModel.CurrentMenuItems;
        SetNavigationViewSelectedItem(ViewModel.CurrentClient.HandlesNavigationSelection && syncMailSelection
            ? ViewModel.SelectedMenuItem
            : null);
    }

    private void UpdateEventDetailsVisualState()
    {
        VisualStateManager.GoToState(this,
            ViewModel.StatePersistenceService.IsEventDetailsVisible ? StateEventDetailsContent : StateDefaultShellContent,
            false);
    }

    private async void NewCalendarEventNavigationItemTapped(object sender, TappedRoutedEventArgs e)
    {
        e.Handled = true;
        await InvokeNewCalendarEventAsync();
    }

    private async void AttentionIconClicked(object sender, RoutedEventArgs e)
    {
        if (sender is not FrameworkElement { DataContext: AccountMenuItem accountMenuItem })
            return;

        if (ViewModel.MailClient is MailAppShellViewModel mailClient)
        {
            await mailClient.HandleAccountAttentionAsync(accountMenuItem.Parameter);
        }
    }

    private async void NewCalendarEventNavigationItemKeyDown(object sender, KeyRoutedEventArgs e)
    {
        if (e.Key is not (VirtualKey.Enter or VirtualKey.Space))
            return;

        e.Handled = true;
        await InvokeNewCalendarEventAsync();
    }

    private Task InvokeNewCalendarEventAsync()
        => ViewModel.CalendarClient.HandleNavigationItemInvokedAsync(new NewCalendarEventMenuItem());

    private async void SynchronizeCalendarsNavigationItemTapped(object sender, TappedRoutedEventArgs e)
    {
        e.Handled = true;
        await InvokeCalendarSynchronizationAsync();
    }

    private async void SynchronizeCalendarsNavigationItemKeyDown(object sender, KeyRoutedEventArgs e)
    {
        if (e.Key is not (VirtualKey.Enter or VirtualKey.Space))
            return;

        e.Handled = true;
        await InvokeCalendarSynchronizationAsync();
    }

    private Task InvokeCalendarSynchronizationAsync()
    {
        if (ViewModel.CalendarClient.SyncCommand.CanExecute(null))
        {
            ViewModel.CalendarClient.SyncCommand.Execute(null);
        }

        return Task.CompletedTask;
    }

    public void Receive(CalendarDisplayTypeChangedMessage message) => NotifyTitleBarContentChanged();

    public void Receive(AccountCreatedMessage message)
    {
        _ = DispatcherQueue.EnqueueAsync(async () =>
        {
            ViewModel.NavigationService.ChangeApplicationMode(WinoApplicationMode.Mail);
            await ViewModel.MailClient.HandleAccountCreatedAsync(message.Account);
        });
    }

    private async void NavigationViewItemInvoked(NavigationView sender, NavigationViewItemInvokedEventArgs args)
    {
        if (_isSyncingNavigationViewSelection)
            return;

        if (ViewModel.IsCalendarMode)
        {
            if (args.InvokedItemContainer is FrameworkElement { DataContext: IMenuItem menuItem })
            {
                await ViewModel.CalendarClient.HandleNavigationItemInvokedAsync(menuItem);
            }

            return;
        }

        if (args.InvokedItemContainer is WinoNavigationViewItem winoNavigationViewItem)
        {
            if (winoNavigationViewItem.SelectsOnInvoked)
                return;

            await ViewModel.CurrentClient.HandleNavigationItemInvokedAsync(winoNavigationViewItem.DataContext as IMenuItem);
        }
    }

    private async void MenuSelectionChanged(NavigationView sender, NavigationViewSelectionChangedEventArgs args)
    {
        if (_isSyncingNavigationViewSelection)
            return;

        if (!ViewModel.CurrentClient.HandlesNavigationSelection)
            return;

        if (ReferenceEquals(args.SelectedItem, ViewModel.SelectedMenuItem))
            return;

        if (args.SelectedItem is IMenuItem invokedMenuItem)
        {
            await ViewModel.CurrentClient.HandleNavigationSelectionChangedAsync(invokedMenuItem);
        }
    }

    public void Receive(AccountMenuItemExtended message)
    {
        if (!ViewModel.IsMailMode)
            return;

        _ = DispatcherQueue.EnqueueAsync(async () =>
        {
            if (message.FolderId == default)
                return;

            if (ViewModel.MailClient.MenuItems!.TryGetFolderMenuItem(message.FolderId, out IBaseFolderMenuItem foundMenuItem))
            {
                foundMenuItem.Expand();
                await ViewModel.MailClient.NavigateFolderAsync(foundMenuItem);
                SetNavigationViewSelectedItem(foundMenuItem);

                if (message.NavigateMailItem != null)
                {
                    WeakReferenceMessenger.Default.Send(new MailItemNavigationRequested(message.NavigateMailItem.UniqueId, ScrollToItem: true));
                }

                return;
            }

            if (message.NavigateMailItem == null)
                return;

            if (ViewModel.MailClient.MenuItems!.TryGetAccountMenuItem(message.NavigateMailItem.AssignedAccount.Id, out IAccountMenuItem accountMenuItem))
            {
                await ViewModel.MailClient.ChangeLoadedAccountAsync(accountMenuItem, navigateInbox: false);

                if (ViewModel.MailClient.MenuItems!.TryGetFolderMenuItem(message.FolderId, out IBaseFolderMenuItem accountFolderMenuItem))
                {
                    accountFolderMenuItem.Expand();
                    await ViewModel.MailClient.NavigateFolderAsync(accountFolderMenuItem);
                    SetNavigationViewSelectedItem(accountFolderMenuItem);
                    WeakReferenceMessenger.Default.Send(new MailItemNavigationRequested(message.NavigateMailItem.UniqueId, ScrollToItem: true));
                }
            }
        });
    }

    public void Receive(NavigateMailFolderEvent message)
    {
        if (!ViewModel.IsMailMode || message.BaseFolderMenuItem == null)
            return;

        if (navigationView.SelectedItem != message.BaseFolderMenuItem)
        {
            var navigateFolderArgs = new NavigateMailFolderEventArgs(message.BaseFolderMenuItem, message.FolderInitLoadAwaitTask);

            ViewModel.NavigationService.Navigate(WinoPage.MailListPage, navigateFolderArgs, NavigationReferenceFrame.InnerShellFrame);

            SetNavigationViewSelectedItem(message.BaseFolderMenuItem);
        }
        else
        {
            message.FolderInitLoadAwaitTask?.TrySetResult(true);
        }
    }

    private void ShellFrameContentNavigated(object sender, NavigationEventArgs e)
    {
        NotifyTitleBarContentChanged();

        if (ViewModel.IsMailMode)
        {
            RefreshNavigationViewBindings();
        }
    }

    private async void MenuItemContextRequested(UIElement sender, ContextRequestedEventArgs args)
    {
        if (!ViewModel.IsMailMode)
            return;

        if (sender is WinoNavigationViewItem menuItem &&
            menuItem.DataContext is IBaseFolderMenuItem baseFolderMenuItem &&
            baseFolderMenuItem.IsMoveTarget &&
            args.TryGetPosition(sender, out Point p))
        {
            args.Handled = true;

            var source = new TaskCompletionSource<FolderOperationMenuItem>();
            var actions = ViewModel.MailClient.GetFolderContextMenuActions(baseFolderMenuItem);
            var flyout = new FolderOperationFlyout(actions, source);

            flyout.ShowAt(menuItem, new FlyoutShowOptions
            {
                ShowMode = FlyoutShowMode.Standard,
                Position = new Point(p.X + 30, p.Y - 20)
            });

            var operation = await source.Task;
            flyout.Dispose();

            if (operation != null)
            {
                await ViewModel.MailClient.PerformFolderOperationAsync(operation.Operation, baseFolderMenuItem);
            }
        }
    }

    public void Receive(CreateNewMailWithMultipleAccountsRequested message)
    {
        if (!ViewModel.IsMailMode)
            return;

        var container = navigationView.ContainerFromMenuItem(ViewModel.MailClient.CreatePrimaryMenuItem);
        var flyout = new AccountSelectorFlyout(message.AllAccounts, ViewModel.MailClient.CreateNewMailForAsync);

        flyout.ShowAt(container, new FlyoutShowOptions
        {
            ShowMode = FlyoutShowMode.Auto,
            Placement = FlyoutPlacementMode.Right
        });
    }

    private void NavigationPaneOpening(NavigationView sender, object args)
    {
        if (!ViewModel.IsMailMode)
            return;

        if (sender.DisplayMode == NavigationViewDisplayMode.Minimal && sender.SelectedItem is IFolderMenuItem selectedFolderMenuItem)
        {
            selectedFolderMenuItem.Expand();
        }
    }

    private void NavigationPaneOpened(NavigationView sender, object args)
        => UpdateNavigationPaneLayout(sender.DisplayMode);

    private void NavigationPaneClosed(NavigationView sender, object args)
        => UpdateNavigationPaneLayout(sender.DisplayMode);

    private void NavigationViewDisplayModeChanged(NavigationView sender, NavigationViewDisplayModeChangedEventArgs args)
        => UpdateNavigationPaneLayout(args.DisplayMode);

    private void MailClientPropertyChanged(object? sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == nameof(IShellClient.SelectedMenuItem) && ViewModel.IsMailMode)
        {
            SetNavigationViewSelectedItem(ViewModel.MailClient.SelectedMenuItem);
        }
    }

    private void CalendarClientPropertyChanged(object? sender, PropertyChangedEventArgs e)
    {
        if (!DispatcherQueue.HasThreadAccess)
        {
            var enqueued = DispatcherQueue.TryEnqueue(() => CalendarClientPropertyChanged(sender, e));

            if (!enqueued)
                throw new InvalidOperationException("Could not marshal calendar property changes onto the UI thread.");

            return;
        }

        if (e.PropertyName == nameof(ICalendarShellClient.CurrentVisibleRange) ||
            e.PropertyName == nameof(ICalendarShellClient.VisibleDateRangeText))
        {
            SynchronizeVisibleDateRangeCalendar();
            UpdateTitleBarSubtitle();
        }
    }

    private void VisibleDateRangeCalendarViewSelectedDatesChanged(CalendarView sender, CalendarViewSelectedDatesChangedEventArgs args)
    {
        if (_isSynchronizingVisibleDateRangeCalendar)
            return;

        DateTimeOffset? interactedDate = null;

        if (args.AddedDates.Count > 0)
        {
            interactedDate = args.AddedDates[0];
        }
        else if (args.RemovedDates.Count > 0)
        {
            interactedDate = args.RemovedDates[0];
        }

        if (interactedDate is null)
            return;

        var clickedArgs = new CalendarViewDayClickedEventArgs(interactedDate.Value.DateTime);

        if (ViewModel.CalendarClient.DateClickedCommand.CanExecute(clickedArgs))
        {
            ViewModel.CalendarClient.DateClickedCommand.Execute(clickedArgs);
        }
    }

    private void SynchronizeVisibleDateRangeCalendar()
    {
        if (!DispatcherQueue.HasThreadAccess)
        {
            var enqueued = DispatcherQueue.TryEnqueue(SynchronizeVisibleDateRangeCalendar);

            if (!enqueued)
                throw new InvalidOperationException("Could not marshal visible date range calendar synchronization onto the UI thread.");

            return;
        }

        _isSynchronizingVisibleDateRangeCalendar = true;

        try
        {
            VisibleDateRangeCalendarView.FirstDayOfWeek = MapFirstDayOfWeek(ViewModel.PreferencesService.FirstDayOfWeek);
            VisibleDateRangeCalendarView.SelectedDates.Clear();

            var currentRange = ViewModel.CalendarClient.CurrentVisibleRange;
            if (currentRange == null)
                return;

            foreach (var date in currentRange.Dates)
            {
                VisibleDateRangeCalendarView.SelectedDates.Add(new DateTimeOffset(date.ToDateTime(TimeOnly.MinValue)));
            }

            VisibleDateRangeCalendarView.SetDisplayDate(new DateTimeOffset(currentRange.AnchorDate.ToDateTime(TimeOnly.MinValue)));
        }
        finally
        {
            _isSynchronizingVisibleDateRangeCalendar = false;
        }
    }

    private void PreferencesServiceChanged(object? sender, string propertyName)
    {
        if (propertyName == nameof(IPreferencesService.FirstDayOfWeek))
        {
            SynchronizeVisibleDateRangeCalendar();
        }
    }

    private static Windows.Globalization.DayOfWeek MapFirstDayOfWeek(DayOfWeek dayOfWeek)
        => dayOfWeek switch
        {
            DayOfWeek.Sunday => Windows.Globalization.DayOfWeek.Sunday,
            DayOfWeek.Monday => Windows.Globalization.DayOfWeek.Monday,
            DayOfWeek.Tuesday => Windows.Globalization.DayOfWeek.Tuesday,
            DayOfWeek.Wednesday => Windows.Globalization.DayOfWeek.Wednesday,
            DayOfWeek.Thursday => Windows.Globalization.DayOfWeek.Thursday,
            DayOfWeek.Friday => Windows.Globalization.DayOfWeek.Friday,
            DayOfWeek.Saturday => Windows.Globalization.DayOfWeek.Saturday,
            _ => Windows.Globalization.DayOfWeek.Monday
        };

    private void ViewModelPropertyChanged(object? sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName != nameof(ViewModel.SelectedMenuItem) || !ViewModel.CurrentClient.HandlesNavigationSelection)
            return;

        SetNavigationViewSelectedItem(ViewModel.SelectedMenuItem);
    }

    private void SetNavigationViewSelectedItem(object? item)
    {
        if (ReferenceEquals(navigationView.SelectedItem, item))
            return;

        _isSyncingNavigationViewSelection = true;
        try
        {
            navigationView.SelectedItem = item;
        }
        finally
        {
            _isSyncingNavigationViewSelection = false;
        }
    }

    private void StatePersistenceServiceChanged(object? sender, string propertyName)
    {
        if (propertyName == nameof(IStatePersistanceService.CalendarDisplayType))
        {
            NotifyTitleBarContentChanged();
            return;
        }

        if (propertyName == nameof(IStatePersistanceService.DayDisplayCount))
        {
            NotifyTitleBarContentChanged();
            return;
        }

        if (propertyName == nameof(IStatePersistanceService.IsEventDetailsVisible))
        {
            UpdateEventDetailsVisualState();
            NotifyTitleBarContentChanged();
        }
    }

    private static void NotifyTitleBarContentChanged()
        => WeakReferenceMessenger.Default.Send(new TitleBarShellContentUpdated());

    private void UpdateNavigationPaneLayout(NavigationViewDisplayMode displayMode)
    {
        if (displayMode == NavigationViewDisplayMode.Expanded && navigationView.IsPaneOpen)
        {
            if (ViewModel.IsCalendarMode)
            {
                PaneCustomContent.Visibility = Visibility.Visible;
                CalendarPaneContent.Visibility = Visibility.Visible;
                ContactsPaneContent.Visibility = Visibility.Collapsed;
                InnerShellFrame.Margin = new Thickness(0);
                return;
            }

            if (ViewModel.IsContactsMode)
            {
                PaneCustomContent.Visibility = Visibility.Visible;
                CalendarPaneContent.Visibility = Visibility.Collapsed;
                ContactsPaneContent.Visibility = Visibility.Visible;
                InnerShellFrame.Margin = new Thickness(0);
                return;
            }
        }

        CalendarPaneContent.Visibility = Visibility.Collapsed;
        ContactsPaneContent.Visibility = Visibility.Collapsed;
        PaneCustomContent.Visibility = Visibility.Collapsed;
        InnerShellFrame.Margin = displayMode == NavigationViewDisplayMode.Minimal
            ? new Thickness(7, 0, 0, 0)
            : new Thickness(0);
    }

    private async void OnPreviewKeyDown(object sender, KeyRoutedEventArgs e)
    {
        if (e.KeyStatus.RepeatCount > 1 || ShouldIgnoreShortcut())
            return;

        var key = NormalizeKey(e.Key);
        if (string.IsNullOrEmpty(key))
            return;

        var mode = ViewModel.CurrentMode;
        var shortcutService = WinoApplication.Current.Services.GetRequiredService<IKeyboardShortcutService>();
        var shortcut = await shortcutService.GetShortcutForKeyAsync(mode, key, GetCurrentModifierKeys());

        if (shortcut == null)
            return;

        var details = new KeyboardShortcutTriggerDetails
        {
            ShortcutId = shortcut.Id,
            Mode = shortcut.Mode,
            Action = shortcut.Action,
            Key = shortcut.Key,
            ModifierKeys = shortcut.ModifierKeys,
            Sender = sender,
            Origin = FocusManager.GetFocusedElement(XamlRoot)
        };

        await ViewModel.CurrentClient.KeyboardShortcutHook(details);

        if (InnerShellFrame.Content is BasePage activePage && activePage.AssociatedViewModel != null)
        {
            await activePage.AssociatedViewModel.KeyboardShortcutHook(details);
        }

        if (details.Handled)
        {
            e.Handled = true;
        }
    }

    private bool ShouldIgnoreShortcut()
    {
        var focusedElement = FocusManager.GetFocusedElement(XamlRoot);

        if (focusedElement is TextBox or AutoSuggestBox or PasswordBox or RichEditBox or ComboBox)
            return true;

        if (focusedElement is FrameworkElement frameworkElement)
        {
            var typeName = frameworkElement.GetType().Name;
            if (typeName.Contains("WebView", StringComparison.OrdinalIgnoreCase))
                return true;
        }

        return false;
    }

    private async void ItemDroppedOnFolder(object sender, DragEventArgs e)
    {
        if (sender is WinoNavigationViewItem droppedContainer)
        {
            droppedContainer.IsDraggingItemOver = false;

            if (CanContinueDragDrop(droppedContainer, e) && droppedContainer.DataContext is IBaseFolderMenuItem draggingFolder)
            {
                var dragPackage = e.DataView.Properties[nameof(MailDragPackage)] as MailDragPackage;
                if (dragPackage == null)
                    return;

                e.AcceptedOperation = Windows.ApplicationModel.DataTransfer.DataPackageOperation.Move;
                var mailCopies = ExtractMailCopies(dragPackage).ToList();
                await ViewModel.MailClient.PerformMoveOperationAsync(mailCopies, draggingFolder);
            }
        }
    }

    private void ItemDragLeaveFromFolder(object sender, DragEventArgs e)
    {
        if (sender is WinoNavigationViewItem leavingContainer)
        {
            leavingContainer.IsDraggingItemOver = false;
        }
    }

    private bool CanContinueDragDrop(WinoNavigationViewItem interactingContainer, DragEventArgs args)
    {
        if (!ViewModel.IsMailMode || !args.DataView.Properties.ContainsKey(nameof(MailDragPackage)))
            return false;

        var dragPackage = args.DataView.Properties[nameof(MailDragPackage)] as MailDragPackage;
        if (dragPackage == null || !dragPackage.DraggingMails.Any())
            return false;

        if (interactingContainer.IsSelected)
            return false;

        if (interactingContainer.DataContext is not IBaseFolderMenuItem folderMenuItem || !folderMenuItem.IsMoveTarget)
            return false;

        var draggedAccountIds = folderMenuItem.HandlingFolders.Select(a => a.MailAccountId);
        var draggedMails = ExtractMailCopies(dragPackage).ToList();

        return draggedMails.Any() && draggedMails.Any(a => draggedAccountIds.Contains(a.AssignedAccount.Id));
    }

    private static IEnumerable<MailCopy> ExtractMailCopies(MailDragPackage dragPackage)
    {
        foreach (var item in dragPackage.DraggingMails)
        {
            if (item is MailCopy mailCopy)
            {
                yield return mailCopy;
            }
            else if (item is MailItemViewModel singleMailItemViewModel)
            {
                yield return singleMailItemViewModel.MailCopy;
            }
            else if (item is ThreadMailItemViewModel threadViewModel)
            {
                foreach (var threadMail in threadViewModel.ThreadEmails)
                {
                    yield return threadMail.MailCopy;
                }
            }
        }
    }

    private void ItemDragEnterOnFolder(object sender, DragEventArgs e)
    {
        if (sender is WinoNavigationViewItem droppedContainer && CanContinueDragDrop(droppedContainer, e))
        {
            droppedContainer.IsDraggingItemOver = true;

            if (droppedContainer.DataContext is IBaseFolderMenuItem draggingFolder)
            {
                e.AcceptedOperation = Windows.ApplicationModel.DataTransfer.DataPackageOperation.Move;
                e.DragUIOverride.Caption = string.Format(Translator.DragMoveToFolderCaption, draggingFolder.FolderName);
            }
        }
    }

    private static ModifierKeys GetCurrentModifierKeys()
    {
        var modifiers = ModifierKeys.None;

        if (Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(Windows.System.VirtualKey.Control).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down))
            modifiers |= ModifierKeys.Control;
        if (Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(Windows.System.VirtualKey.Menu).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down))
            modifiers |= ModifierKeys.Alt;
        if (Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(Windows.System.VirtualKey.Shift).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down))
            modifiers |= ModifierKeys.Shift;
        if (Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(Windows.System.VirtualKey.LeftWindows).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down) ||
            Microsoft.UI.Input.InputKeyboardSource.GetKeyStateForCurrentThread(Windows.System.VirtualKey.RightWindows).HasFlag(Windows.UI.Core.CoreVirtualKeyStates.Down))
        {
            modifiers |= ModifierKeys.Windows;
        }

        return modifiers;
    }

    private static string NormalizeKey(Windows.System.VirtualKey key)
    {
        return key switch
        {
            Windows.System.VirtualKey.Control or
            Windows.System.VirtualKey.LeftControl or
            Windows.System.VirtualKey.RightControl or
            Windows.System.VirtualKey.Menu or
            Windows.System.VirtualKey.LeftMenu or
            Windows.System.VirtualKey.RightMenu or
            Windows.System.VirtualKey.Shift or
            Windows.System.VirtualKey.LeftShift or
            Windows.System.VirtualKey.RightShift or
            Windows.System.VirtualKey.LeftWindows or
            Windows.System.VirtualKey.RightWindows => string.Empty,
            _ => key.ToString()
        };
    }

    protected override void RegisterRecipients()
    {
        base.RegisterRecipients();

        WeakReferenceMessenger.Default.Register<AccountMenuItemExtended>(this);
        WeakReferenceMessenger.Default.Register<CreateNewMailWithMultipleAccountsRequested>(this);
        WeakReferenceMessenger.Default.Register<NavigateMailFolderEvent>(this);
        WeakReferenceMessenger.Default.Register<CalendarDisplayTypeChangedMessage>(this);
        WeakReferenceMessenger.Default.Register<AccountCreatedMessage>(this);
    }

    protected override void UnregisterRecipients()
    {
        base.UnregisterRecipients();

        WeakReferenceMessenger.Default.Unregister<AccountMenuItemExtended>(this);
        WeakReferenceMessenger.Default.Unregister<CreateNewMailWithMultipleAccountsRequested>(this);
        WeakReferenceMessenger.Default.Unregister<NavigateMailFolderEvent>(this);
        WeakReferenceMessenger.Default.Unregister<CalendarDisplayTypeChangedMessage>(this);
        WeakReferenceMessenger.Default.Unregister<AccountCreatedMessage>(this);
    }
}
