using System.Linq;
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.Store;

public partial class Products : ComponentBase, IDisposable
{
    [Inject] private ProductService ProductService { get; set; } = default!;
    [Inject] private CategoryService CategoryService { get; set; } = default!;
    [Inject] private CartService Cart { get; set; } = default!;
    [Inject] private VATService VAT { 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 _query = string.Empty;
    private bool _loading;
    private bool _loadingMore;
    private bool _hasMore = true;
    private int _currentPage = 1;
    private int _totalCount;
    private const int PageSize = 12;
    private List<Product> _products = new();
    private long? _activeCategoryId;
    private string? _activeCategoryTitle;
    private ProductSortOption _sortOption = ProductSortOption.PriceDesc;
    private bool _ignoreNextLocationChange;
    private bool _pendingScrollRestore;

    protected override async Task OnInitializedAsync()
    {
        _isAuthenticated = await AuthService.IsAuthenticatedAsync();
        await Cart.EnsureInitializedAsync();
        Cart.OnChange += StateHasChanged;
        Navigation.LocationChanged += HandleLocationChanged;
        ApplyStateFromUri();
        await LoadPages(_currentPage);
        _pendingScrollRestore = true;
    }

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
            await VAT.LoadAsync();

        if (_pendingScrollRestore && !_loading && _products.Count > 0)
        {
            _pendingScrollRestore = false;
            var payload = await ShopListScrollRestore.TakeAsync(Js, ShopListScrollRestore.StoreKey);
            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));
        _query = state.Query;
        _sortOption = ShopListQueryState.ParseSortOption(state.Sort);
        _activeCategoryId = state.CategoryId;
        _currentPage = state.Pages;
    }

    private ShopListQueryState CaptureState() => new()
    {
        Query = _query,
        Sort = ShopListQueryState.ToSortKey(_sortOption),
        CategoryId = _activeCategoryId,
        Pages = Math.Max(1, _currentPage)
    };

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

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

    private async Task LoadPages(int pagesToLoad)
    {
        _loading = true;
        _products.Clear();
        _hasMore = true;
        pagesToLoad = Math.Max(1, pagesToLoad);
        var sortBy = ShopListQueryState.ToApiSortBy(_sortOption);

        for (var page = 1; page <= pagesToLoad; page++)
        {
            var result = await ProductService.GetProductsPagedAsync(
                _query, _activeCategoryId, sortBy, page, PageSize);
            if (page == 1)
            {
                _products = result.Products;
                _totalCount = result.TotalCount;
            }
            else
            {
                _products.AddRange(result.Products);
            }

            _currentPage = page;
            _hasMore = result.HasNext;
            if (!_hasMore)
                break;
        }

        _activeCategoryTitle = _activeCategoryId is { } categoryId
            ? (await CategoryService.GetByIdAsync(categoryId))?.Title
            : null;
        _loading = false;
    }

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

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

        _loadingMore = true;
        StateHasChanged();

        _currentPage++;
        var sortBy = ShopListQueryState.ToApiSortBy(_sortOption);
        var result = await ProductService.GetProductsPagedAsync(
            _query, _activeCategoryId, sortBy, _currentPage, PageSize);
        _products.AddRange(result.Products);
        _hasMore = result.HasNext;
        SyncUrl();

        _loadingMore = false;
        StateHasChanged();
    }

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

    private async Task OnQueryChanged(KeyboardEventArgs _) => await ReloadFromFilters();

    private async Task AddToCart(Product p)
    {
        await GuestGate.RunAsync(() => Cart.Add(p, 1));
    }

    private async Task OpenProduct(long productId)
    {
        await ShopListScrollRestore.SaveAsync(Js, ShopListScrollRestore.StoreKey, productId);
        Navigation.NavigateTo($"{RouteConstants.Store.ProductDetail}{productId}");
    }

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

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

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

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

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

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

    private async Task ClearCategoryFilter()
    {
        _activeCategoryId = null;
        _activeCategoryTitle = null;
        await ReloadFromFilters();
    }
}
