namespace ServiceLib.ViewModels;

public partial class AddGroupServerViewModel : MyReactiveObject, ICloseable
{
    public event EventHandler? RequestClose;

    [Reactive]
    public partial ProfileItem SelectedSource { get; set; }

    [Reactive]
    public partial ProfileItem SelectedChild { get; set; }

    [Reactive]
    public partial IList<ProfileItem> SelectedChildren { get; set; }

    [Reactive]
    public partial string? CoreType { get; set; }

    [Reactive]
    public partial string? PolicyGroupType { get; set; }

    [Reactive]
    public partial SubItem? SelectedSubItem { get; set; }

    [Reactive]
    public partial string? Filter { get; set; }

    public BulkObservableCollection<SubItem> SubItems { get; } = [];

    public BulkObservableCollection<ProfileItem> ChildItemsObs { get; } = [];

    public BulkObservableCollection<ProfileItem> AllProfilePreviewItemsObs { get; } = [];

    public ReactiveCommand<RxVoid, RxVoid> AddCmd { get; }
    public ReactiveCommand<RxVoid, RxVoid> RemoveCmd { get; }

    public ReactiveCommand<RxVoid, RxVoid> MoveTopCmd { get; }
    public ReactiveCommand<RxVoid, RxVoid> MoveUpCmd { get; }
    public ReactiveCommand<RxVoid, RxVoid> MoveDownCmd { get; }
    public ReactiveCommand<RxVoid, RxVoid> MoveBottomCmd { get; }

    public ReactiveCommand<RxVoid, RxVoid> SaveCmd { get; }

    public AddGroupServerViewModel(ProfileItem profileItem)
    {
        _config = AppManager.Instance.Config;

        var canEditRemove = this.WhenAnyValue(
            x => x.SelectedChild,
            selectedChild => selectedChild != null && !selectedChild.Remarks.IsNullOrEmpty());

        AddCmd = ReactiveCommand.CreateFromTask(async () =>
        {
            await AddChildAsync();
        });
        RemoveCmd = ReactiveCommand.CreateFromTask(async () =>
        {
            await ChildRemoveAsync();
        }, canEditRemove);
        MoveTopCmd = ReactiveCommand.CreateFromTask(async () =>
        {
            await MoveServer(EMove.Top);
        }, canEditRemove);
        MoveUpCmd = ReactiveCommand.CreateFromTask(async () =>
        {
            await MoveServer(EMove.Up);
        }, canEditRemove);
        MoveDownCmd = ReactiveCommand.CreateFromTask(async () =>
        {
            await MoveServer(EMove.Down);
        }, canEditRemove);
        MoveBottomCmd = ReactiveCommand.CreateFromTask(async () =>
        {
            await MoveServer(EMove.Bottom);
        }, canEditRemove);
        SaveCmd = ReactiveCommand.CreateFromTask(async () =>
        {
            await SaveServerAsync();
        });

        SelectedSource = profileItem.IndexId.IsNullOrEmpty() ? profileItem : JsonUtils.DeepCopy(profileItem);
        CoreType = (SelectedSource?.CoreType ?? ECoreType.Xray).ToString();

        _ = Init();
    }

    public async Task Init()
    {
        var protocolExtra = SelectedSource.GetProtocolExtra();
        PolicyGroupType = (protocolExtra?.MultipleLoad ?? EMultipleLoad.LeastPing) switch
        {
            EMultipleLoad.LeastPing => ResUI.TbLeastPing,
            EMultipleLoad.Fallback => ResUI.TbFallback,
            EMultipleLoad.Random => ResUI.TbRandom,
            EMultipleLoad.RoundRobin => ResUI.TbRoundRobin,
            EMultipleLoad.LeastLoad => ResUI.TbLeastLoad,
            _ => ResUI.TbLeastPing,
        };

        var subs = await AppManager.Instance.SubItems();
        subs.Add(new SubItem());
        SubItems.AddRange(subs);
        SelectedSubItem = SubItems.FirstOrDefault(s => s.Id == protocolExtra?.SubChildItems);
        Filter = protocolExtra?.Filter;

        var childIndexIds = Utils.String2List(protocolExtra?.ChildItems) ?? [];
        var childItemList = await AppManager.Instance.GetProfileItemsOrderedByIndexIds(childIndexIds);
        ChildItemsObs.AddRange(childItemList);
    }

    public async Task AddChildAsync()
    {
        var profileSelectViewModel = new ProfilesSelectViewModel();
        profileSelectViewModel.SetConfigTypeFilter([EConfigType.Custom], exclude: true);
        profileSelectViewModel.MultiSelect = true;
        var result = await AppManager.Instance.WindowDialog.ShowDialogAsync(profileSelectViewModel);
        if (result != true)
        {
            return;
        }
        var profiles = await profileSelectViewModel.GetProfileItems() ?? [];
        ChildItemsObs.AddRange(profiles);
    }

    public async Task ChildRemoveAsync()
    {
        if (SelectedChild == null || SelectedChild.IndexId.IsNullOrEmpty())
        {
            NoticeManager.Instance.Enqueue(ResUI.PleaseSelectServer);
            return;
        }
        foreach (var it in SelectedChildren ?? [SelectedChild])
        {
            if (it != null)
            {
                ChildItemsObs.Remove(it);
            }
        }
        await Task.CompletedTask;
    }

    public async Task MoveServer(EMove eMove)
    {
        if (SelectedChild == null || SelectedChild.IndexId.IsNullOrEmpty())
        {
            NoticeManager.Instance.Enqueue(ResUI.PleaseSelectServer);
            return;
        }
        var index = ChildItemsObs.IndexOf(SelectedChild);
        if (index < 0)
        {
            return;
        }
        var selectedChild = JsonUtils.DeepCopy(SelectedChild);
        switch (eMove)
        {
            case EMove.Top:
                if (index == 0)
                {
                    return;
                }
                ChildItemsObs.RemoveAt(index);
                ChildItemsObs.Insert(0, selectedChild);
                break;

            case EMove.Up:
                if (index == 0)
                {
                    return;
                }
                ChildItemsObs.RemoveAt(index);
                ChildItemsObs.Insert(index - 1, selectedChild);
                break;

            case EMove.Down:
                if (index == ChildItemsObs.Count - 1)
                {
                    return;
                }
                ChildItemsObs.RemoveAt(index);
                ChildItemsObs.Insert(index + 1, selectedChild);
                break;

            case EMove.Bottom:
                if (index == ChildItemsObs.Count - 1)
                {
                    return;
                }
                ChildItemsObs.RemoveAt(index);
                ChildItemsObs.Add(selectedChild);
                break;

            default:
                break;
        }
        await Task.CompletedTask;
    }

    private ProtocolExtraItem GetUpdatedProtocolExtra()
    {
        return SelectedSource.GetProtocolExtra() with
        {
            ChildItems =
            Utils.List2String(ChildItemsObs.Where(s => !s.IndexId.IsNullOrEmpty()).Select(s => s.IndexId).ToList()),
            MultipleLoad = PolicyGroupType switch
            {
                var s when s == ResUI.TbLeastPing => EMultipleLoad.LeastPing,
                var s when s == ResUI.TbFallback => EMultipleLoad.Fallback,
                var s when s == ResUI.TbRandom => EMultipleLoad.Random,
                var s when s == ResUI.TbRoundRobin => EMultipleLoad.RoundRobin,
                var s when s == ResUI.TbLeastLoad => EMultipleLoad.LeastLoad,
                _ => EMultipleLoad.LeastPing,
            },
            SubChildItems = SelectedSubItem?.Id,
            Filter = Filter,
        };
    }

    public async Task UpdatePreviewList()
    {
        AllProfilePreviewItemsObs.Clear();
        AllProfilePreviewItemsObs.AddRange(await GroupProfileManager.GetChildProfileItemsByProtocolExtra(GetUpdatedProtocolExtra()));
    }

    private async Task SaveServerAsync()
    {
        var remarks = SelectedSource.Remarks;
        if (remarks.IsNullOrEmpty())
        {
            NoticeManager.Instance.Enqueue(ResUI.PleaseFillRemarks);
            return;
        }
        if (ChildItemsObs.Count == 0 && SelectedSubItem?.Id.IsNullOrEmpty() == true)
        {
            NoticeManager.Instance.Enqueue(ResUI.PleaseAddAtLeastOneServer);
            return;
        }
        SelectedSource.CoreType = CoreType.IsNullOrEmpty() ? ECoreType.Xray : Enum.Parse<ECoreType>(CoreType);
        if (SelectedSource.CoreType is not (ECoreType.Xray or ECoreType.sing_box) ||
            SelectedSource.ConfigType is not (EConfigType.ProxyChain or EConfigType.PolicyGroup))
        {
            return;
        }

        var protocolExtra = GetUpdatedProtocolExtra();

        SelectedSource.SetProtocolExtra(protocolExtra);

        if (await ConfigHandler.AddServerCommon(_config, SelectedSource) == 0)
        {
            NoticeManager.Instance.Enqueue(ResUI.OperationSuccess);
            RequestClose?.Invoke(this, EventArgs.Empty);
        }
        else
        {
            NoticeManager.Instance.Enqueue(ResUI.OperationFailed);
        }
    }
}
