using CommunityToolkit.Mvvm.ComponentModel;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media.Animation;
using Microsoft.UI.Xaml.Navigation;
using Sonex.Client.Dialogs;
using Sonex.Data.Database;
using Sonex.Data.Messages;
using Sonex.Data.Records;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Logger = Sonex.Client.Helpers.Logger;
using Sonex.Client.Helpers;

// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.

namespace Sonex.Client.AppWindows;

internal sealed class BreadcrumbItemInfo
{
    public BreadcrumbItemInfo(string title, NavigationViewItem navigationItem)
    {
        Title = title;
        NavigationItem = navigationItem;
    }

    public string Title { get; }
    public NavigationViewItem NavigationItem { get; }
}

/// <summary>
/// An empty window that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainWindow : Window
{
    private bool _notificationsStarted;
    private List<BreadcrumbItemInfo> _breadcrumbItems = new();

    public MainWindow()
    {
        InitializeComponent();
        this.ExtendsContentIntoTitleBar = true; // Extend the content into the title bar and hide the default title bar
        this.AppWindow.TitleBar.PreferredHeightOption = Microsoft.UI.Windowing.TitleBarHeightOption.Tall;
        this.SetTitleBar(titleBar); // Set the custom title bar
        string iconPath = System.IO.Path.Combine(AppContext.BaseDirectory, "Assets\\AppIcon.ico");
        this.AppWindow.SetIcon(iconPath);
        Closed += MainWindow_Closed;
        DB.Notifications.NotificationReceived += Notifications_NotificationReceived;
        DB.Notifications.ListenerError += Notifications_ListenerError;
        BreadcrumbBar1.ItemsSource = _breadcrumbItems;

        var initialItem = GetFirstNavigableItem(navView.MenuItems);
        if (initialItem != null)
            NavigateToNavigationItem(initialItem);
    }
    private AccountRecord? account;
    private void MainWindow_SizeChanged(object sender, WindowSizeChangedEventArgs args)
    {
        if (Logo.Visibility == Visibility.Visible && args.Size.Width < 500)
        {
            Logo.Visibility = Visibility.Collapsed;
            return;
        }

        if (Logo.Visibility == Visibility.Collapsed && args.Size.Width > 500)
        {
            Logo.Visibility = Visibility.Visible;
            return;
        }
    }

    private void NavFrame_Navigated(object sender, NavigationEventArgs e)
    {
        if (e.SourcePageType == null)
        {
            Logger.SetCurrentPage(null, null);
            UpdateBreadcrumbs(null);
            return;
        }

        var item = navView.SelectedItem as NavigationViewItem;
        if (item == null || GetPageType(item) != e.SourcePageType)
            item = FindItemByPageType(navView.MenuItems, e.SourcePageType);

        if (item != null && !ReferenceEquals(navView.SelectedItem, item))
            navView.SelectedItem = item;

        Logger.SetCurrentPage(
            e.SourcePageType.FullName ?? e.SourcePageType.Name,
            item != null ? GetNavigationItemTitle(item) : null);
        UpdateBreadcrumbs(item);
    }

    private void TitleBar_BackRequested(TitleBar sender, object args)
    {
        if (navFrame.CanGoBack)
        {
            navFrame.GoBack(new SuppressNavigationTransitionInfo());
        }
    }

    private void navView_SelectionChanged(NavigationView sender, NavigationViewSelectionChangedEventArgs args)
    {
        
        if (args.SelectedItem is not NavigationViewItem item)
            return;

        NavigateToNavigationItem(item);
    }

    private void BreadcrumbBar1_ItemClicked(BreadcrumbBar sender, BreadcrumbBarItemClickedEventArgs args)
    {
        if (args.Index < 0 || args.Index >= _breadcrumbItems.Count)
            return;

        NavigateToNavigationItem(_breadcrumbItems[args.Index].NavigationItem);
    }

    private async void NavigateToNavigationItem(NavigationViewItem item)
    {

        GC.Collect();
        GC.WaitForPendingFinalizers();
        GC.Collect();
        if (GlobalValues.isEditing)
        {
            var result = await this.ShowQuestionAsync("Unsaved changes", "You have unsaved changes. Are you sure you want to leave this page?", "Yes", "No");
            if (result != ContentDialogResult.Primary)
            {
                GlobalValues.isEditing = false;
                return;
            }
        }
        var navigableItem = GetNavigableItem(item);
        if (navigableItem == null)
            return;

        var pageType = GetPageType(navigableItem);
        if (pageType == null)
            return;

        if (!ReferenceEquals(navView.SelectedItem, navigableItem))
            navView.SelectedItem = navigableItem;

        if (navFrame.CurrentSourcePageType != pageType)
        {
            navFrame.Navigate(pageType, null, new SuppressNavigationTransitionInfo());

            return;
        }

        UpdateBreadcrumbs(navigableItem);
    }

    private void UpdateBreadcrumbs(NavigationViewItem? item)
    {
        if (item == null)
        {
            _breadcrumbItems = new List<BreadcrumbItemInfo>();
            BreadcrumbBar1.ItemsSource = _breadcrumbItems;
            return;
        }

        var path = new List<NavigationViewItem>();
        if (!TryBuildNavigationPath(navView.MenuItems, item, path))
            path.Add(item);

        _breadcrumbItems = path
            .Select(current => new BreadcrumbItemInfo(GetNavigationItemTitle(current), current))
            .ToList();

        BreadcrumbBar1.ItemsSource = _breadcrumbItems;
    }

    private static bool TryBuildNavigationPath(IList<object> items, NavigationViewItem target, List<NavigationViewItem> path)
    {
        foreach (var obj in items)
        {
            if (obj is not NavigationViewItem item)
                continue;

            path.Add(item);
            if (ReferenceEquals(item, target))
                return true;

            if (TryBuildNavigationPath(item.MenuItems, target, path))
                return true;

            path.RemoveAt(path.Count - 1);
        }

        return false;
    }

    private static NavigationViewItem? FindItemByPageType(IList<object> items, Type pageType)
    {
        foreach (var obj in items)
        {
            if (obj is not NavigationViewItem item)
                continue;

            if (GetPageType(item) == pageType)
                return item;

            var childItem = FindItemByPageType(item.MenuItems, pageType);
            if (childItem != null)
                return childItem;
        }

        return null;
    }

    private static NavigationViewItem? GetFirstNavigableItem(IList<object> items)
    {
        foreach (var obj in items)
        {
            if (obj is not NavigationViewItem item)
                continue;

            var navigableItem = GetNavigableItem(item);
            if (navigableItem != null)
                return navigableItem;
        }

        return null;
    }

    private static NavigationViewItem? GetNavigableItem(NavigationViewItem item)
    {
        if (GetPageType(item) != null)
            return item;

        return GetFirstNavigableItem(item.MenuItems);
    }

    private static Type? GetPageType(NavigationViewItem item)
    {
        var tag = item.Tag as string;
        if (string.IsNullOrWhiteSpace(tag))
            return null;

        return typeof(MainWindow).Assembly.GetType("Sonex.Client.Pages." + tag.Trim());
    }

    private static string GetNavigationItemTitle(NavigationViewItem item)
    {
        return item.Content switch
        {
            string title when !string.IsNullOrWhiteSpace(title) => title,
            TextBlock textBlock when !string.IsNullOrWhiteSpace(textBlock.Text) => textBlock.Text,
            _ => item.Tag as string ?? string.Empty
        };
    }
    private void TitleBar_PaneToggleRequested(TitleBar sender, object args) => navView.IsPaneOpen = !navView.IsPaneOpen;
    private void ProfileButton_Click(object sender, RoutedEventArgs e) => ProfileButtonTeachingTip.IsOpen = !ProfileButtonTeachingTip.IsOpen;
    private void LogoutButton_Click(object sender, RoutedEventArgs e) => Logout();
    private void ProfileButtonTeachingTip_CloseButtonClick(TeachingTip sender, object args) => Logout();

    private void Logout()
    {
        string? parentDir = Directory.GetParent(AppContext.BaseDirectory)?.FullName;
        if (parentDir == null) return;
        string LancherExe = Path.Combine(parentDir, "Sonex.Launcher.exe");
        if (File.Exists(LancherExe))
        {
            Process.Start(LancherExe);
        }
        Application.Current.Exit();
    }
    private void CloseApp()
    {
        Application.Current.Exit();
    }

    private async void Window_Activated(object sender, WindowActivatedEventArgs args)
    {

        var result = await AccountRecord.GetCurrentAccount();
        if (result.Success && result.Item != null)
        {
            account = result.Item;

            InitialsTextBox.Text = account.Initials;
            ProfileButtonTeachingTip.Title = account.FullName;
            ProfileButtonTeachingTip.Subtitle = account.Department;
            await EnsureNotificationsStartedAsync();
        }
        else
        {
            Debug.WriteLine(result.ErrorMessage ?? "GetCurrentAccount failed.");
        }
    }

    private async void ProfileButtonTeachingTip_ActionButtonClick(TeachingTip sender, object args)
    {
        if (account == null || Content is not FrameworkElement root)
            return;

        ProfileButtonTeachingTip.IsOpen = false;

        var dialog = ChangePasswordDialog.CreateForCurrentAccount(account);
        dialog.XamlRoot = root.XamlRoot;

        await dialog.ShowAsync();
    }

    private async Task EnsureNotificationsStartedAsync()
    {
        if (_notificationsStarted)
            return;

        var instanceId = DB.ClientSessionInfo?.InstanceId;
        if (string.IsNullOrWhiteSpace(instanceId))
            return;

        await DB.Notifications.StartAsync(
            MessagesService.GlobalMessagesChannel,
            MessagesService.GetInstanceChannel(instanceId));
        _notificationsStarted = true;
    }

    private void Notifications_ListenerError(object? sender, Exception ex)
    {
        Logger.Error(
            ex,
            source: nameof(MainWindow),
            operation: "Notifications.ListenerError");
    }

    private void Notifications_NotificationReceived(object? sender, MessageEventArgs e)
    {
        if (DispatcherQueue == null)
            return;

        if (DispatcherQueue.HasThreadAccess)
            _ = HandleNotificationAsync(e.Notification);
        else
            DispatcherQueue.TryEnqueue(() => _ = HandleNotificationAsync(e.Notification));
    }

    private async Task HandleNotificationAsync(Message notification)
    {
        var action = notification.Action?.Trim().ToLowerInvariant();

        if (action == ActiveSessionRecord.LogoutAction)
        {
            Logout();
            return;
        }

        if (action == ActiveSessionRecord.CloseAction)
        {
            CloseApp();
            return;
        }

        var title = string.IsNullOrWhiteSpace(notification.Title)
            ? "Message"
            : notification.Title;

        var content = string.IsNullOrWhiteSpace(notification.Text)
            ? notification.Action
            : notification.Text;

        if (string.IsNullOrWhiteSpace(content))
            return;

        if (Content is not FrameworkElement root)
            return;

        try
        {
            await root.ShowMessageAsync(title, content);
        }
        catch (Exception ex)
        {
            Logger.Error(
                ex,
                source: nameof(MainWindow),
                operation: nameof(HandleNotificationAsync));
        }
    }

    private async void MainWindow_Closed(object sender, WindowEventArgs args)
    {
        DB.Notifications.NotificationReceived -= Notifications_NotificationReceived;
        DB.Notifications.ListenerError -= Notifications_ListenerError;

        if (_notificationsStarted)
            await DB.Notifications.StopAsync();
    }
}
