using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Routing;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.JSInterop;
using FrontOffice.Main.Utilities;

namespace FrontOffice.Main.Pages.DiscountStore;

public partial class Products : ComponentBase, IDisposable
{
    [Inject] private DiscountProductService ProductService { get; set; } = default!;
    [Inject] private DiscountCartService DiscountCart { get; set; } = default!;
    [Inject] private GuestActionGate GuestGate { get; set; } = default!;
    [Inject] private AuthService AuthService { get; set; } = default!;
    [Inject] private IJSRuntime Js { get; set; } = default!;

    private bool _isAuthenticated;
    private string _search = string.Empty;
    private long? _selectedCategoryId;
    private int _currentPage = 1;
    private bool _loading;
    private bool _loadingMore;
    private bool _hasMore = true;
    private int _totalCount;
    private const int PageSize = 12;
    private const string DefaultSortBy = "price desc";

    private List<DiscountProductCard> _products = new();
    private List<DiscountCategoryNode> _categories = new();
    private bool _ignoreNextLocationChange;
    private bool _pendingScrollRestore;

    protected override async Task OnInitializedAsync()
    {
        _isAuthenticated = await AuthService.IsAuthenticatedAsync();
        await DiscountCart.EnsureInitializedAsync();
        DiscountCart.OnChange += StateHasChanged;
        Navigation.LocationChanged += HandleLocationChanged;

        _categories = await ProductService.GetCategoriesAsync();
        ApplyStateFromUri();
        _loading = true;
        await LoadPages(_currentPage);
        _loading = false;
        _pendingScrollRestore = true;
    }

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (_pendingScrollRestore && !_loading && _products.Count > 0)
        {
            _pendingScrollRestore = false;
            var payload = await ShopListScrollRestore.TakeAsync(Js, ShopListScrollRestore.DiscountKey);
            if (payload is not null)
                await ShopListScrollRestore.RestoreAsync(Js, payload);
        }

        await base.OnAfterRenderAsync(firstRender);
    }

    private void ApplyStateFromUri()
    {
        var state = ShopListQueryState.Parse(Navigation.ToAbsoluteUri(Navigation.Uri));
        _search = state.Query;
        _selectedCategoryId = state.CategoryId;
        _currentPage = state.Pages;
    }

    private ShopListQueryState CaptureState() => new()
    {
        Query = _search,
        CategoryId = _selectedCategoryId,
        Pages = Math.Max(1, _currentPage)
    };

    private void SyncUrl()
    {
        var target = CaptureState().ToRelativeUrl(RouteConstants.DiscountStore.Products);
        var currentPathAndQuery = Navigation.ToAbsoluteUri(Navigation.Uri).PathAndQuery;
        if (string.Equals(currentPathAndQuery, target, StringComparison.OrdinalIgnoreCase))
            return;

        _ignoreNextLocationChange = true;
        Navigation.NavigateTo(target, replace: true);
    }

    private async Task LoadPages(int pagesToLoad)
    {
        _products.Clear();
        _hasMore = true;
        pagesToLoad = Math.Max(1, pagesToLoad);
        var search = string.IsNullOrWhiteSpace(_search) ? null : _search;

        for (var page = 1; page <= pagesToLoad; page++)
        {
            var result = await ProductService.GetProductsAsync(
                page: page,
                pageSize: PageSize,
                search: search,
                categoryId: _selectedCategoryId,
                sortBy: DefaultSortBy);

            if (page == 1)
            {
                _products = result.Products;
                _totalCount = result.TotalCount;
            }
            else
            {
                _products.AddRange(result.Products);
            }

            _currentPage = page;
            _hasMore = result.CurrentPage < result.TotalPages;
            if (!_hasMore)
                break;
        }
    }

    private async Task ReloadFromFilters()
    {
        _loading = true;
        _currentPage = 1;
        StateHasChanged();
        await LoadPages(1);
        SyncUrl();
        _loading = false;
        StateHasChanged();
    }

    private async Task LoadMore()
    {
        if (_loadingMore || !_hasMore) return;

        _loadingMore = true;
        StateHasChanged();

        _currentPage++;
        var result = await ProductService.GetProductsAsync(
            page: _currentPage,
            pageSize: PageSize,
            search: string.IsNullOrWhiteSpace(_search) ? null : _search,
            categoryId: _selectedCategoryId,
            sortBy: DefaultSortBy);
        _products.AddRange(result.Products);
        _hasMore = result.CurrentPage < result.TotalPages;
        SyncUrl();

        _loadingMore = false;
        StateHasChanged();
    }

    private async Task SearchProducts() => await ReloadFromFilters();

    private async Task OnSearchKeyUp(KeyboardEventArgs e)
    {
        if (e.Key == "Enter")
            await ReloadFromFilters();
    }

    private async Task OnCategoryChanged(long? value)
    {
        _selectedCategoryId = value;
        await ReloadFromFilters();
    }

    private async Task AddToCart(DiscountProductCard p)
    {
        await GuestGate.RunAsync(() => DiscountCart.AddAsync(p.Id));
    }

    private async Task NavigateToProduct(long id)
    {
        await ShopListScrollRestore.SaveAsync(Js, ShopListScrollRestore.DiscountKey, id);
        Navigation.NavigateTo($"{RouteConstants.DiscountStore.ProductDetail}{id}");
    }

    private void HandleLocationChanged(object? sender, LocationChangedEventArgs args)
    {
        if (_ignoreNextLocationChange)
        {
            _ignoreNextLocationChange = false;
            return;
        }

        var uri = Navigation.ToAbsoluteUri(args.Location);
        if (!uri.AbsolutePath.Equals(RouteConstants.DiscountStore.Products, StringComparison.OrdinalIgnoreCase))
            return;

        var incoming = ShopListQueryState.Parse(uri);
        if (incoming.Matches(CaptureState()))
            return;

        _ = InvokeAsync(async () =>
        {
            ApplyStateFromUri();
            _loading = true;
            await LoadPages(_currentPage);
            _loading = false;
            _pendingScrollRestore = true;
            StateHasChanged();
        });
    }

    private static string FormatPrice(long price) => $"{price:N0} تومان";

    public void Dispose()
    {
        DiscountCart.OnChange -= StateHasChanged;
        Navigation.LocationChanged -= HandleLocationChanged;
    }
}
