using Netina.Common.Models.Api;
using Netina.Common.Models.Exception;
using Netina.Domain.CommandQueries.Commands;
using Netina.Domain.CommandQueries.Queries;
using Netina.Domain.Dtos.SmallDtos;
using Netina.Domain.Entities.Orders;
using Netina.Domain.Entities.Products;
using Netina.Repository.Abstracts;
using Netina.Repository.Repositories.Base.Contracts;

namespace Netina.Core.EntityServices.OrderBagHandlers;

public class SubmitOrderBagCommandHandler : IRequestHandler<SubmitOrderBagCommand,OrderSDto>
{
    private readonly IMediator _mediator;
    private readonly IRepositoryWrapper _repositoryWrapper;
    private readonly ICurrentUserService _currentUserService;

    public SubmitOrderBagCommandHandler(IMediator mediator, IRepositoryWrapper repositoryWrapper, ICurrentUserService currentUserService)
    {
        _mediator = mediator;
        _repositoryWrapper = repositoryWrapper;
        _currentUserService = currentUserService;
    }

    public async Task<OrderSDto> Handle(SubmitOrderBagCommand request, CancellationToken cancellationToken)
    {
        if (_currentUserService.UserId == null)
            throw new AppException("Customer id notfound", ApiResultStatusCode.BadRequest);
        if (!Guid.TryParse(_currentUserService.UserId, out Guid userId))
            throw new AppException("Customer id wrong", ApiResultStatusCode.BadRequest);

        var orderBag = await _mediator.Send(new GetUserOrderBagQuery(), cancellationToken);

        foreach (var requestDto in request.RequestDtos)
        {

            var product = await _repositoryWrapper.SetRepository<Product>()
                .TableNoTracking
                .FirstOrDefaultAsync(p => p.Id == requestDto.ProductId, cancellationToken);

            if (product == null)
                throw new AppException("Product not found ", ApiResultStatusCode.NotFound);
            if (!product.IsEnable)
                throw new AppException("Product is not enable", ApiResultStatusCode.BadRequest);

            var productSDto = product.AdaptToSDto();
            await _mediator.Send(new CalculateProductDiscountCommand(productSDto), cancellationToken);

            orderBag.ChangeOrderBag(productSDto.Id, productSDto.Cost, productSDto.CostWithDiscount,
                productSDto.HasDiscount, productSDto.PackingCost, productSDto.CategoryId, requestDto.Count);
        }
        foreach (var orderProduct in orderBag.OrderProducts.ToList())
        {
            if(request.RequestDtos.FirstOrDefault(op=>op.ProductId==orderProduct.ProductId)==null)
            {
                orderBag.OrderProducts.Remove(orderProduct);
                _repositoryWrapper.SetRepository<OrderProduct>().Delete(orderProduct);
                await _repositoryWrapper.SaveChangesAsync(cancellationToken);
            }
        }

        _repositoryWrapper.SetRepository<Order>().Update(orderBag);
        await _repositoryWrapper.SaveChangesAsync(cancellationToken);

        var order = await _mediator.Send(new CalculateOrderCommand(orderBag.Id), cancellationToken);

        return order.AdaptToSDto();
    }
}