using CMSMicroservice.Application.DiscountShopCQ.Commands.AddToCustomerCart;
using CMSMicroservice.Application.DiscountShopCQ.Commands.RemoveFromCustomerCart;
using CMSMicroservice.Application.DiscountShopCQ.Commands.UpdateCustomerCartItem;
using CMSMicroservice.Application.DiscountShopCQ.Queries.GetCustomerCart;
using CMSMicroservice.Application.Common.Interfaces;
using CMSMicroservice.Protobuf.Protos.UserCarts;
using CMSMicroservice.WebApi.Common.Services;
using Google.Protobuf.WellKnownTypes;
using Microsoft.EntityFrameworkCore;
using MediatR;
using System.Linq;

namespace CMSMicroservice.WebApi.Services;

public class UserCartsService : UserCartsContract.UserCartsContractBase
{
    private readonly IDispatchRequestToCQRS _dispatcher;
    private readonly ISender _sender;
    private readonly IApplicationDbContext _context;

    public UserCartsService(IDispatchRequestToCQRS dispatcher, ISender sender, IApplicationDbContext context)
    {
        _dispatcher = dispatcher;
        _sender = sender;
        _context = context;
    }

    #region Customer Methods

    public override async Task<AddNewUserCartForCustomerResponse> AddNewUserCartForCustomer(
        AddNewUserCartForCustomerRequest request, ServerCallContext context)
    {
        var command = new AddToCustomerCartCommand
        {
            ProductId = request.ProductId,
            Count = request.Count
        };

        var result = await _sender.Send(command);

        return new AddNewUserCartForCustomerResponse
        {
            Id = result.Id,
            Message = result.Message,
            Success = result.Success
        };
    }

    public override async Task<UpdateUserCartForCustomerResponse> UpdateUserCartForCustomer(
        UpdateUserCartForCustomerRequest request, ServerCallContext context)
    {
        var command = new UpdateCustomerCartItemCommand
        {
            CartItemId = request.CartItemId,
            Count = request.Count
        };

        var result = await _sender.Send(command);

        return new UpdateUserCartForCustomerResponse
        {
            Message = result.Message,
            Success = result.Success
        };
    }

    public override async Task<RemoveUserCartForCustomerResponse> RemoveUserCartForCustomer(
        RemoveUserCartForCustomerRequest request, ServerCallContext context)
    {
        var command = new RemoveFromCustomerCartCommand
        {
            CartItemId = request.CartItemId
        };

        var result = await _sender.Send(command);

        return new RemoveUserCartForCustomerResponse
        {
            Message = result.Message,
            Success = result.Success
        };
    }

    public override async Task<GetUserCartForCustomerResponse> GetCustomerCart(
        GetUserCartForCustomerRequest request, ServerCallContext context)
    {
        var query = new GetCustomerCartQuery();
        var result = await _sender.Send(query);

        var response = new GetUserCartForCustomerResponse
        {
            TotalPrice = result.TotalPrice,
            TotalItemsCount = result.TotalItemsCount,
            Message = result.Message
        };

        foreach (var item in result.Items)
        {
            response.Models.Add(new UserCartItem
            {
                Id = item.Id,
                ProductId = item.ProductId,
                ProductTitle = item.ProductTitle,
                ProductShortInformation = item.ProductShortInformation,
                ProductShortInfomation = item.ProductShortInformation, // Alias for typo compatibility
                ProductPrice = item.ProductPrice,
                ProductDiscount = item.ProductDiscount,
                ProductThumbnailPath = item.ProductThumbnailPath,
                Count = item.Count,
                TotalItemPrice = item.TotalItemPrice,
                Created = Timestamp.FromDateTime(DateTime.SpecifyKind(item.Created, DateTimeKind.Utc))
            });
        }

        return response;
    }

    #endregion

    #region Admin Methods

    public override async Task<AddNewUserCartResponse> AddNewUserCart(
        AddNewUserCartRequest request, ServerCallContext context)
    {
        var entity = new Domain.Entities.UserCart
        {
            ProductId = request.ProductId,
            UserId = request.UserId,
            Count = request.Count
        };
        
        _context.UserCarts.Add(entity);
        await _context.SaveChangesAsync(context.CancellationToken);
        
        return new AddNewUserCartResponse { Id = entity.Id };
    }

    public override async Task<Empty> UpdateUserCart(
        UpdateUserCartRequest request, ServerCallContext context)
    {
        var cartId = request.Id > 0 ? request.Id : request.UserCartId;
        var cart = await _context.UserCarts.FindAsync(new object[] { cartId }, context.CancellationToken);
        if (cart == null)
            throw new RpcException(new Status(StatusCode.NotFound, "آیتم سبد خرید یافت نشد"));
        
        cart.Count = request.Count;
        await _context.SaveChangesAsync(context.CancellationToken);
        return new Empty();
    }

    public override async Task<Empty> DeleteUserCart(
        DeleteUserCartRequest request, ServerCallContext context)
    {
        var cart = await _context.UserCarts.FindAsync(new object[] { request.Id }, context.CancellationToken);
        if (cart == null)
            throw new RpcException(new Status(StatusCode.NotFound, "آیتم سبد خرید یافت نشد"));
        
        cart.IsDeleted = true;
        await _context.SaveChangesAsync(context.CancellationToken);
        return new Empty();
    }

    public override async Task<GetUserCartResponse> GetUserCart(
        GetUserCartRequest request, ServerCallContext context)
    {
        var cart = await _context.UserCarts
            .Where(c => c.Id == request.Id && !c.IsDeleted)
            .FirstOrDefaultAsync(context.CancellationToken);
        
        if (cart == null)
            throw new RpcException(new Status(StatusCode.NotFound, "آیتم سبد خرید یافت نشد"));
        
        return new GetUserCartResponse
        {
            Id = cart.Id,
            ProductId = cart.ProductId,
            UserId = cart.UserId,
            Count = cart.Count
        };
    }

    public override async Task<GetAllUserCartsByFilterResponse> GetAllUserCartsByFilter(
        GetAllUserCartsByFilterRequest request, ServerCallContext context)
    {
        var pageNumber = request.PaginationState?.PageNumber ?? 1;
        var pageSize = request.PaginationState?.PageSize ?? 20;
        if (pageNumber < 1) pageNumber = 1;
        if (pageSize < 1) pageSize = 20;
        
        var query = _context.UserCarts
            .Include(c => c.Product)
            .Where(c => !c.IsDeleted);
        
        if (request.Filter?.Id != null)
            query = query.Where(c => c.Id == request.Filter.Id.Value);
        if (request.Filter?.ProductId != null)
            query = query.Where(c => c.ProductId == request.Filter.ProductId.Value);
        if (request.Filter?.UserId != null)
            query = query.Where(c => c.UserId == request.Filter.UserId.Value);
        
        var totalCount = await query.CountAsync(context.CancellationToken);
        
        var items = await query
            .OrderByDescending(c => c.Created)
            .Skip((pageNumber - 1) * pageSize)
            .Take(pageSize)
            .Select(c => new GetAllUserCartsByFilterResponseModel
            {
                Id = c.Id,
                ProductId = c.ProductId,
                UserId = c.UserId,
                Count = c.Count,
                ProductTitle = c.Product != null ? c.Product.Title : string.Empty,
                ProductShortInfomation = c.Product != null ? (c.Product.ShortInfomation ?? string.Empty) : string.Empty,
                ProductPrice = c.Product != null ? c.Product.Price : 0,
                ProductDiscount = c.Product != null ? c.Product.Discount : 0,
                ProductThumbnailPath = c.Product != null ? (c.Product.ThumbnailPath ?? string.Empty) : string.Empty,
                Created = Timestamp.FromDateTime(DateTime.SpecifyKind(c.Created, DateTimeKind.Utc))
            })
            .ToListAsync(context.CancellationToken);
        
        return new GetAllUserCartsByFilterResponse
        {
            MetaData = new CMSMicroservice.Protobuf.Protos.MetaData
            {
                CurrentPage = pageNumber,
                PageSize = pageSize,
                TotalCount = totalCount,
                TotalPage = (int)Math.Ceiling(totalCount / (double)pageSize),
                HasPrevious = pageNumber > 1,
                HasNext = pageNumber * pageSize < totalCount
            },
            Models = { items }
        };
    }

    #endregion
}
