using System.Text.Json;
using CMSMicroservice.Protobuf.Protos;
using CMSMicroservice.Protobuf.Protos.Category;
using BackOffice.Common.BaseComponents;
using BackOffice.Common.Utilities;
using Mapster;
using Microsoft.AspNetCore.Components;
using MudBlazor;
using DataModel = CMSMicroservice.Protobuf.Protos.Category.GetAllCategoryByFilterResponseModel;

namespace BackOffice.Pages.Category;

public partial class CategoryMainPage
{
    [Inject] public CategoryContract.CategoryContractClient CategoryContract { get; set; } = default!;

    private BasePageComponent _basePage = default!;
    private MudDataGrid<DataModel> _gridData = default!;
    private GetAllCategoryByFilterRequest _request = new() { Filter = new() };
    private long? _parentFilterId;
    private readonly Dictionary<long, string> _parentTitles = new();
    private readonly List<CategoryTreeNode> _treeNodes = new();

    private sealed class CategoryTreeNode
    {
        public long Id { get; set; }
        public string Title { get; set; } = string.Empty;
        public long? ParentId { get; set; }
        public int Level { get; set; }
    }

    protected override async Task OnInitializedAsync()
    {
        await LoadTreeAsync();
    }

    private async Task<GridData<DataModel>> ServerReload(GridState<DataModel> state)
    {
        _request.Filter ??= new();
        _request.PaginationState ??= new();
        _request.PaginationState.PageNumber = state.Page + 1;
        _request.PaginationState.PageSize = state.PageSize;

        var result = await CategoryContract.GetAllCategoryByFilterAsync(_request);
        if (result != null && result.Models != null && result.Models.Any())
        {
            await EnsureParentTitlesAsync(result.Models);

            return new GridData<DataModel>
            {
                Items = result.Models.ToList(),
                TotalItems = (int)result.MetaData.TotalCount
            };
        }

        return new GridData<DataModel>();
    }

    private async Task LoadTreeAsync()
    {
        var request = new GetAllCategoryByFilterRequest
        {
            Filter = new GetAllCategoryByFilterFilter(),
            PaginationState = new PaginationState
            {
                PageNumber = 1,
                PageSize = 1000
            }
        };

        var response = await CategoryContract.GetAllCategoryByFilterAsync(request);
        var models = response?.Models?.ToList() ?? new List<DataModel>();

        var lookup = models
            .GroupBy(x => x.ParentId ?? 0)
            .ToDictionary(g => g.Key,
                g => g.OrderBy(c => c.SortOrder).ThenBy(c => c.Title).ToList());

        _treeNodes.Clear();

        void AddChildren(long? parentId, int level)
        {
            var key = parentId ?? 0;
            if (!lookup.TryGetValue(key, out var children))
                return;

            foreach (var child in children)
            {
                _treeNodes.Add(new CategoryTreeNode
                {
                    Id = child.Id,
                    Title = child.Title,
                    ParentId = parentId,
                    Level = level
                });

                AddChildren(child.Id, level + 1);
            }
        }

        AddChildren(null, 0);
    }

    private async Task EnsureParentTitlesAsync(IEnumerable<DataModel> items)
    {
        var parentIds = items
            .Where(x => x.ParentId.HasValue && x.ParentId.Value > 0 && !_parentTitles.ContainsKey(x.ParentId.Value))
            .Select(x => x.ParentId.Value)
            .Distinct()
            .ToList();

        if (!parentIds.Any())
        {
            return;
        }

        foreach (var id in parentIds)
        {
            try
            {
                var response = await CategoryContract.GetCategoryAsync(new GetCategoryRequest { Id = id });
                if (response != null)
                {
                    _parentTitles[id] = response.Title;
                }
            }
            catch
            {
                // ignore individual parent load failures
            }
        }

        StateHasChanged();
    }

    public async Task CreateNew()
    {
        var dialog = await DialogService.ShowAsync<CreateOrUpdateCategoryDialog>(
            "افزودن دسته‌بندی",
            new DialogParameters<CreateOrUpdateCategoryDialog>
            {
                { x => x.Model, new CreateNewCategoryRequest { IsActive = true } }
            },
            new DialogOptions { CloseButton = true, FullWidth = true, MaxWidth = MaxWidth.Small });

        var result = await dialog.Result;
        if (!result.Canceled)
        {
            await ReloadData();
            await LoadTreeAsync();
            Snackbar.Add("دسته‌بندی با موفقیت ایجاد شد.", Severity.Success);
        }
    }

    public async Task CreateSubcategory(DataModel parent)
    {
        var dialog = await DialogService.ShowAsync<CreateOrUpdateCategoryDialog>(
            $"افزودن زیردسته برای «{parent.Title}»",
            new DialogParameters<CreateOrUpdateCategoryDialog>
            {
                { x => x.Model, new CreateNewCategoryRequest { IsActive = true, ParentId = parent.Id } }
            },
            new DialogOptions { CloseButton = true, FullWidth = true, MaxWidth = MaxWidth.Small });

        var result = await dialog.Result;
        if (!result.Canceled)
        {
            await ReloadData();
            await LoadTreeAsync();
            Snackbar.Add("زیردسته با موفقیت ایجاد شد.", Severity.Success);
        }
    }

    public async Task Update(DataModel model)
    {
        var getResponse = await CategoryContract.GetCategoryAsync(new GetCategoryRequest { Id = model.Id });

        var editModel = getResponse.Adapt<UpdateCategoryRequest>();
        var dialog = await DialogService.ShowAsync<CreateOrUpdateCategoryDialog>(
            "ویرایش دسته‌بندی",
            new DialogParameters<CreateOrUpdateCategoryDialog>
            {
                { x => x.EditModel, editModel }
            },
            new DialogOptions { CloseButton = true, FullWidth = true, MaxWidth = MaxWidth.Small });

        var result = await dialog.Result;
        if (!result.Canceled)
        {
            await ReloadData();
            await LoadTreeAsync();
            Snackbar.Add("دسته‌بندی با موفقیت ویرایش شد.", Severity.Success);
        }
    }

    private async Task OnDelete(DataModel model)
    {
        var options = new DialogOptions { CloseOnEscapeKey = true, MaxWidth = MaxWidth.Small };
        bool? result = await DialogService.ShowMessageBox(
            "اخطار",
            $"آیا از حذف دسته‌بندی «{model.Title}» مطمئن هستید؟",
            yesText: "حذف",
            cancelText: "لغو",
            options: options);

        if (result == true)
        {
            await CategoryContract.DeleteCategoryAsync(new DeleteCategoryRequest { Id = model.Id });
            await ReloadData();
            await LoadTreeAsync();
            Snackbar.Add("دسته‌بندی با موفقیت حذف شد.", Severity.Success);
        }

        StateHasChanged();
    }

    private async Task ReloadData()
    {
        if (_gridData != null)
        {
            await _gridData.ReloadServerData();
        }
    }

    public async Task OnFilterSubmit()
    {
        _basePage.IsFiltered = true;
        StateHasChanged();
        await ReloadData();
    }

    public async Task OnFilterCleared()
    {
        _basePage.IsFiltered = false;
        StateHasChanged();
        _request = new GetAllCategoryByFilterRequest { Filter = new GetAllCategoryByFilterFilter() };
        _parentFilterId = null;
        await ReloadData();
    }

    public Task OnParentFilterChanged(long? parentId)
    {
        _parentFilterId = parentId;
        _request.Filter ??= new GetAllCategoryByFilterFilter();
        _request.Filter.ParentId = parentId;
        return Task.CompletedTask;
    }

    public async Task OnTreeNodeSelected(long? id)
    {
        await OnParentFilterChanged(id);
        await ReloadData();
    }

    public void OpenCategoryProducts(DataModel model)
    {
        Navigation.NavigateTo($"{RouteConstance.CategoryProducts}{model.Id}");
    }

    private bool HasChildren(long categoryId)
    {
        return _treeNodes.Any(n => n.ParentId == categoryId);
    }
}