using DialogHostAvalonia;
using v2rayN.Desktop.Common;

namespace v2rayN.Desktop.Views;

public partial class ProfilesView : ReactiveUserControl<ProfilesViewModel>
{
    private static Config _config;
    private Window? _window;

    public ProfilesView()
    {
        InitializeComponent();
    }

    public ProfilesView(Window window)
    {
        InitializeComponent();

        _config = AppManager.Instance.Config;
        _window = window;

        menuSelectAll.Click += menuSelectAll_Click;
        btnAutofitColumnWidth.Click += BtnAutofitColumnWidth_Click;
        txtServerFilter.KeyDown += TxtServerFilter_KeyDown;
        lstProfiles.KeyDown += LstProfiles_KeyDown;
        lstProfiles.SelectionChanged += lstProfiles_SelectionChanged;
        lstProfiles.DoubleTapped += LstProfiles_DoubleTapped;
        lstProfiles.LoadingRow += LstProfiles_LoadingRow;
        lstProfiles.Sorting += LstProfiles_Sorting;
        //if (_config.uiItem.enableDragDropSort)
        //{
        //    lstProfiles.AllowDrop = true;
        //    lstProfiles.PreviewMouseLeftButtonDown += LstProfiles_PreviewMouseLeftButtonDown;
        //    lstProfiles.MouseMove += LstProfiles_MouseMove;
        //    lstProfiles.DragEnter += LstProfiles_DragEnter;
        //    lstProfiles.Drop += LstProfiles_Drop;
        //}

        ViewModel = new ProfilesViewModel(UpdateViewHandler);

        this.WhenActivated(disposables =>
        {
            this.OneWayBind(ViewModel, vm => vm.ProfileItems, v => v.lstProfiles.ItemsSource).DisposeWith(disposables);
            this.Bind(ViewModel, vm => vm.SelectedProfile, v => v.lstProfiles.SelectedItem).DisposeWith(disposables);

            // this.OneWayBind(ViewModel, vm => vm.SubItems, v => v.lstGroup.ItemsSource).DisposeWith(disposables);
            this.Bind(ViewModel, vm => vm.SelectedSub, v => v.lstGroup.SelectedItem).DisposeWith(disposables);
            this.Bind(ViewModel, vm => vm.ServerFilter, v => v.txtServerFilter.Text).DisposeWith(disposables);
            this.BindCommand(ViewModel, vm => vm.AddSubCmd, v => v.btnAddSub).DisposeWith(disposables);
            this.BindCommand(ViewModel, vm => vm.EditSubCmd, v => v.btnEditSub).DisposeWith(disposables);
            this.BindCommand(ViewModel, vm => vm.EditSubCmd, v => v.menuSubEdit).DisposeWith(disposables);
            this.BindCommand(ViewModel, vm => vm.AddSubCmd, v => v.menuSubAdd).DisposeWith(disposables);
            this.BindCommand(ViewModel, vm => vm.DeleteSubCmd, v => v.menuSubDelete).DisposeWith(disposables);

            //servers delete
            this.BindCommand(ViewModel, vm => vm.EditServerCmd, v => v.menuEditServer).DisposeWith(disposables);
            this.BindCommand(ViewModel, vm => vm.RemoveServerCmd, v => v.menuRemoveServer).DisposeWith(disposables);
            this.BindCommand(ViewModel, vm => vm.RemoveDuplicateServerCmd, v => v.menuRemoveDuplicateServer).DisposeWith(disposables);
            this.BindCommand(ViewModel, vm => vm.CopyServerCmd, v => v.menuCopyServer).DisposeWith(disposables);
            this.BindCommand(ViewModel, vm => vm.SetDefaultServerCmd, v => v.menuSetDefaultServer).DisposeWith(disposables);
            this.BindCommand(ViewModel, vm => vm.ShareServerCmd, v => v.menuShareServer).DisposeWith(disposables);
            this.BindCommand(ViewModel, vm => vm.GenGroupMultipleServerXrayRandomCmd, v => v.menuGenGroupMultipleServerXrayRandom).DisposeWith(disposables);
            this.BindCommand(ViewModel, vm => vm.GenGroupMultipleServerXrayRoundRobinCmd, v => v.menuGenGroupMultipleServerXrayRoundRobin).DisposeWith(disposables);
            this.BindCommand(ViewModel, vm => vm.GenGroupMultipleServerXrayLeastPingCmd, v => v.menuGenGroupMultipleServerXrayLeastPing).DisposeWith(disposables);
            this.BindCommand(ViewModel, vm => vm.GenGroupMultipleServerXrayLeastLoadCmd, v => v.menuGenGroupMultipleServerXrayLeastLoad).DisposeWith(disposables);
            this.BindCommand(ViewModel, vm => vm.GenGroupMultipleServerSingBoxLeastPingCmd, v => v.menuGenGroupMultipleServerSingBoxLeastPing).DisposeWith(disposables);
            this.BindCommand(ViewModel, vm => vm.GenGroupMultipleServerSingBoxFallbackCmd, v => v.menuGenGroupMultipleServerSingBoxFallback).DisposeWith(disposables);

            //servers move
            //this.OneWayBind(ViewModel, vm => vm.SubItems, v => v.cmbMoveToGroup.ItemsSource).DisposeWith(disposables);
            //this.Bind(ViewModel, vm => vm.SelectedMoveToGroup, v => v.cmbMoveToGroup.SelectedItem).DisposeWith(disposables);

            this.BindCommand(ViewModel, vm => vm.MoveTopCmd, v => v.menuMoveTop).DisposeWith(disposables);
            this.BindCommand(ViewModel, vm => vm.MoveUpCmd, v => v.menuMoveUp).DisposeWith(disposables);
            this.BindCommand(ViewModel, vm => vm.MoveDownCmd, v => v.menuMoveDown).DisposeWith(disposables);
            this.BindCommand(ViewModel, vm => vm.MoveBottomCmd, v => v.menuMoveBottom).DisposeWith(disposables);

            //servers ping
            this.BindCommand(ViewModel, vm => vm.MixedTestServerCmd, v => v.menuMixedTestServer).DisposeWith(disposables);
            this.BindCommand(ViewModel, vm => vm.TcpingServerCmd, v => v.menuTcpingServer).DisposeWith(disposables);
            this.BindCommand(ViewModel, vm => vm.RealPingServerCmd, v => v.menuRealPingServer).DisposeWith(disposables);
            this.BindCommand(ViewModel, vm => vm.SpeedServerCmd, v => v.menuSpeedServer).DisposeWith(disposables);
            this.BindCommand(ViewModel, vm => vm.SortServerResultCmd, v => v.menuSortServerResult).DisposeWith(disposables);
            this.BindCommand(ViewModel, vm => vm.RemoveInvalidServerResultCmd, v => v.menuRemoveInvalidServerResult).DisposeWith(disposables);
            this.BindCommand(ViewModel, vm => vm.FastRealPingCmd, v => v.btnFastRealPing).DisposeWith(disposables);

            //servers export
            this.BindCommand(ViewModel, vm => vm.Export2ClientConfigCmd, v => v.menuExport2ClientConfig).DisposeWith(disposables);
            this.BindCommand(ViewModel, vm => vm.Export2ClientConfigClipboardCmd, v => v.menuExport2ClientConfigClipboard).DisposeWith(disposables);
            this.BindCommand(ViewModel, vm => vm.Export2ShareUrlCmd, v => v.menuExport2ShareUrl).DisposeWith(disposables);
            this.BindCommand(ViewModel, vm => vm.Export2ShareUrlBase64Cmd, v => v.menuExport2ShareUrlBase64).DisposeWith(disposables);

            AppEvents.AppExitRequested
              .AsObservable()
              .ObserveOn(RxApp.MainThreadScheduler)
              .Subscribe(_ => StorageUI())
              .DisposeWith(disposables);

            AppEvents.AdjustMainLvColWidthRequested
                .AsObservable()
                .ObserveOn(RxApp.MainThreadScheduler)
                .Subscribe(_ => AutofitColumnWidth())
                .DisposeWith(disposables);
        });

        RestoreUI();
    }

    private async void LstProfiles_Sorting(object? sender, DataGridColumnEventArgs e)
    {
        e.Handled = true;

        if (ViewModel != null && e.Column?.Tag?.ToString() != null)
        {
            await ViewModel.SortServer(e.Column.Tag.ToString());
        }

        e.Handled = false;
    }

    #region Event

    private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
    {
        switch (action)
        {
            case EViewAction.SetClipboardData:
                if (obj is null)
                {
                    return false;
                }

                await AvaUtils.SetClipboardData(this, (string)obj);
                break;

            case EViewAction.ProfilesFocus:
                lstProfiles.Focus();
                break;

            case EViewAction.ShowYesNo:
                if (await UI.ShowYesNo(_window, ResUI.RemoveServer) != ButtonResult.Yes)
                {
                    return false;
                }
                break;

            case EViewAction.SaveFileDialog:
                if (obj is null)
                {
                    return false;
                }

                var fileName = await UI.SaveFileDialog(_window, "");
                if (fileName.IsNullOrEmpty())
                {
                    return false;
                }
                ViewModel?.Export2ClientConfigResult(fileName, (ProfileItem)obj);
                break;

            case EViewAction.AddServerWindow:
                if (obj is null)
                {
                    return false;
                }

                return await new AddServerWindow((ProfileItem)obj).ShowDialog<bool>(_window);

            case EViewAction.AddServer2Window:
                if (obj is null)
                {
                    return false;
                }

                return await new AddServer2Window((ProfileItem)obj).ShowDialog<bool>(_window);

            case EViewAction.AddGroupServerWindow:
                if (obj is null)
                {
                    return false;
                }

                return await new AddGroupServerWindow((ProfileItem)obj).ShowDialog<bool>(_window);

            case EViewAction.ShareServer:
                if (obj is null)
                {
                    return false;
                }

                await ShareServer((string)obj);
                break;

            case EViewAction.SubEditWindow:
                if (obj is null)
                {
                    return false;
                }

                return await new SubEditWindow((SubItem)obj).ShowDialog<bool>(_window);

            case EViewAction.DispatcherRefreshServersBiz:
                Dispatcher.UIThread.Post(RefreshServersBiz, DispatcherPriority.Default);
                break;
        }

        return await Task.FromResult(true);
    }

    public async Task ShareServer(string url)
    {
        if (url.IsNullOrEmpty())
        {
            return;
        }

        var dialog = new QrcodeView(url);
        await DialogHost.Show(dialog);
    }

    public void RefreshServersBiz()
    {
        if (lstProfiles.SelectedIndex >= 0)
        {
            lstProfiles.ScrollIntoView(lstProfiles.SelectedItem, null);
        }
    }

    private void lstProfiles_SelectionChanged(object? sender, SelectionChangedEventArgs e)
    {
        if (ViewModel != null)
        {
            ViewModel.SelectedProfiles = lstProfiles.SelectedItems.Cast<ProfileItemModel>().ToList();
        }
    }

    private void LstProfiles_DoubleTapped(object? sender, Avalonia.Input.TappedEventArgs e)
    {
        var source = e.Source as Border;
        if (source?.Name == "HeaderBackground")
        {
            return;
        }

        if (_config.UiItem.DoubleClick2Activate)
        {
            ViewModel?.SetDefaultServer();
        }
        else
        {
            ViewModel?.EditServerAsync();
        }
    }

    private void LstProfiles_LoadingRow(object? sender, DataGridRowEventArgs e)
    {
        e.Row.Header = $" {e.Row.Index + 1}";
    }

    //private void LstProfiles_ColumnHeader_Click(object? sender, RoutedEventArgs e)
    //{
    //    var colHeader = sender as DataGridColumnHeader;
    //    if (colHeader == null || colHeader.TabIndex < 0 || colHeader.Column == null)
    //    {
    //        return;
    //    }

    //    var colName = ((MyDGTextColumn)colHeader.Column).ExName;
    //    ViewModel?.SortServer(colName);
    //}

    private void menuSelectAll_Click(object? sender, RoutedEventArgs e)
    {
        lstProfiles.SelectAll();
    }

    private void LstProfiles_KeyDown(object? sender, KeyEventArgs e)
    {
        if (e.KeyModifiers is KeyModifiers.Control or KeyModifiers.Meta)
        {
            switch (e.Key)
            {
                case Key.A:
                    menuSelectAll_Click(null, null);
                    break;

                case Key.C:
                    ViewModel?.Export2ShareUrlAsync(false);
                    break;

                case Key.D:
                    ViewModel?.EditServerAsync();
                    break;

                case Key.F:
                    ViewModel?.ShareServerAsync();
                    break;

                case Key.O:
                    ViewModel?.ServerSpeedtest(ESpeedActionType.Tcping);
                    break;

                case Key.R:
                    ViewModel?.ServerSpeedtest(ESpeedActionType.Realping);
                    break;

                case Key.T:
                    ViewModel?.ServerSpeedtest(ESpeedActionType.Speedtest);
                    break;

                case Key.E:
                    ViewModel?.ServerSpeedtest(ESpeedActionType.Mixedtest);
                    break;
            }
        }
        else
        {
            switch (e.Key)
            {
                case Key.Enter:
                    //case Key.Return:
                    ViewModel?.SetDefaultServer();
                    break;

                case Key.Delete:
                case Key.Back:
                    ViewModel?.RemoveServerAsync();
                    break;

                case Key.T:
                    ViewModel?.MoveServer(EMove.Top);
                    break;

                case Key.U:
                    ViewModel?.MoveServer(EMove.Up);
                    break;

                case Key.D:
                    ViewModel?.MoveServer(EMove.Down);
                    break;

                case Key.B:
                    ViewModel?.MoveServer(EMove.Bottom);
                    break;

                case Key.Escape:
                    ViewModel?.ServerSpeedtestStop();
                    break;
            }
        }
    }

    private void BtnAutofitColumnWidth_Click(object? sender, RoutedEventArgs e)
    {
        AutofitColumnWidth();
    }

    private void AutofitColumnWidth()
    {
        try
        {
            //First scroll horizontally to the initial position to avoid the control crash bug
            if (lstProfiles.SelectedIndex >= 0)
            {
                lstProfiles.ScrollIntoView(lstProfiles.SelectedItem, lstProfiles.Columns[0]);
            }
            else
            {
                var model = lstProfiles.ItemsSource.Cast<ProfileItemModel>();
                if (model.Any())
                {
                    lstProfiles.ScrollIntoView(model.First(), lstProfiles.Columns[0]);
                }
                else
                {
                    return;
                }
            }

            foreach (var it in lstProfiles.Columns)
            {
                it.Width = new DataGridLength(1, DataGridLengthUnitType.Auto);
            }
        }
        catch (Exception ex)
        {
            Logging.SaveLog("ProfilesView", ex);
        }
    }

    private void TxtServerFilter_KeyDown(object? sender, KeyEventArgs e)
    {
        if (e.Key is Key.Enter or Key.Return)
        {
            ViewModel?.RefreshServers();
        }
    }

    #endregion Event

    #region UI

    private void RestoreUI()
    {
        var lvColumnItem = _config.UiItem.MainColumnItem.OrderBy(t => t.Index).ToList();
        var displayIndex = 0;
        foreach (var item in lvColumnItem)
        {
            foreach (var item2 in lstProfiles.Columns)
            {
                if (item2.Tag == null)
                {
                    continue;
                }
                if (item2.Tag.Equals(item.Name))
                {
                    if (item.Width < 0)
                    {
                        item2.IsVisible = false;
                    }
                    else
                    {
                        item2.Width = new DataGridLength(item.Width, DataGridLengthUnitType.Pixel);
                        item2.DisplayIndex = displayIndex++;
                    }
                    if (item.Name.ToLower().StartsWith("to"))
                    {
                        item2.IsVisible = _config.GuiItem.EnableStatistics;
                    }
                }
            }
        }
    }

    private void StorageUI()
    {
        List<ColumnItem> lvColumnItem = new();
        foreach (var item2 in lstProfiles.Columns)
        {
            if (item2.Tag == null)
            {
                continue;
            }
            lvColumnItem.Add(new()
            {
                Name = (string)item2.Tag,
                Width = (int)(item2.IsVisible == true ? item2.ActualWidth : -1),
                Index = item2.DisplayIndex
            });
        }
        _config.UiItem.MainColumnItem = lvColumnItem;
    }

    #endregion UI

    #region Drag and Drop

    //private Point startPoint = new();
    //private int startIndex = -1;
    //private string formatData = "ProfileItemModel";

    ///// <summary>
    ///// Helper to search up the VisualTree
    ///// </summary>
    ///// <typeparam name="T"></typeparam>
    ///// <param name="current"></param>
    ///// <returns></returns>
    //private static T? FindAncestor<T>(DependencyObject current) where T : DependencyObject
    //{
    //    do
    //    {
    //        if (current is T)
    //        {
    //            return (T)current;
    //        }
    //        current = VisualTreeHelper.GetParent(current);
    //    }
    //    while (current != null);
    //    return null;
    //}

    //private void LstProfiles_PreviewMouseLeftButtonDown(object? sender, MouseButtonEventArgs e)
    //{
    //    // Get current mouse position
    //    startPoint = e.GetPosition(null);
    //}

    //private void LstProfiles_MouseMove(object? sender, MouseEventArgs e)
    //{
    //    // Get the current mouse position
    //    Point mousePos = e.GetPosition(null);
    //    Vector diff = startPoint - mousePos;

    //    if (e.LeftButton == MouseButtonState.Pressed &&
    //        (Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance ||
    //               Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance))
    //    {
    //        // Get the dragged Item
    //        if (sender is not DataGrid listView) return;
    //        var listViewItem = FindAncestor<DataGridRow>((DependencyObject)e.OriginalSource);
    //        if (listViewItem == null) return;           // Abort
    //                                                    // Find the data behind the ListViewItem
    //        ProfileItemModel item = (ProfileItemModel)listView.ItemContainerGenerator.ItemFromContainer(listViewItem);
    //        if (item == null) return;                   // Abort
    //                                                    // Initialize the drag & drop operation
    //        startIndex = lstProfiles.SelectedIndex;
    //        DataObject dragData = new(formatData, item);
    //        DragDrop.DoDragDrop(listViewItem, dragData, DragDropEffects.Copy | DragDropEffects.Move);
    //    }
    //}

    //private void LstProfiles_DragEnter(object? sender, DragEventArgs e)
    //{
    //    if (!e.Data.GetDataPresent(formatData) || sender != e.Source)
    //    {
    //        e.Effects = DragDropEffects.None;
    //    }
    //}

    //private void LstProfiles_Drop(object? sender, DragEventArgs e)
    //{
    //    if (e.Data.GetDataPresent(formatData) && sender == e.Source)
    //    {
    //        // Get the drop Item destination
    //        if (sender is not DataGrid listView) return;
    //        var listViewItem = FindAncestor<DataGridRow>((DependencyObject)e.OriginalSource);
    //        if (listViewItem == null)
    //        {
    //            // Abort
    //            e.Effects = DragDropEffects.None;
    //            return;
    //        }
    //        // Find the data behind the Item
    //        ProfileItemModel item = (ProfileItemModel)listView.ItemContainerGenerator.ItemFromContainer(listViewItem);
    //        if (item == null) return;
    //        // Move item into observable collection
    //        // (this will be automatically reflected to lstView.ItemsSource)
    //        e.Effects = DragDropEffects.Move;

    //        ViewModel?.MoveServerTo(startIndex, item);

    //        startIndex = -1;
    //    }
    //}

    #endregion Drag and Drop
}
