using System.Text.Json;
using BasketService.Models;
using BasketService.Repositories;
using Grpc.Core;
using GrpcBasket;
using RabbitMQ.Client;
namespace BasketService;

public class BasketService(IBasketRepository repository, IConfiguration configuration, IServiceProvider serviceProvider, ILogger<BasketService> logger) : Basket.BasketBase
{
    private readonly IBasketRepository _repository = repository;
    private readonly IConfiguration _configuration = configuration;
    private readonly ILogger<BasketService> _logger = logger;
    private IConnection? _messageConnection;

    public override async Task<CustomerBasketResponse> GetBasketById(BasketRequest request, ServerCallContext context)
    {
        // Uncomment to force a delay for testing resiliency, etc.
        //await Task.Delay(5000);

        var data = await _repository.GetBasketAsync(request.Id);

        if (data != null)
        {
            return MapToCustomerBasketResponse(data);
        }

        return new CustomerBasketResponse();
    }

    public override async Task<CustomerBasketResponse?> UpdateBasket(CustomerBasketRequest request, ServerCallContext context)
    {
        var customerBasket = MapToCustomerBasket(request);
        var response = await _repository.UpdateBasketAsync(customerBasket);

        if (response is null)
        {
            throw new RpcException(new Status(StatusCode.NotFound, $"Basket with buyer id {request.BuyerId} does not exist"));
        }

        return MapToCustomerBasketResponse(response);
    }

    public override async Task<CheckoutCustomerBasketResponse> CheckoutBasket(CheckoutCustomerBasketRequest request, ServerCallContext context)
    {
        var buyerId = request.BuyerId;
        var basket = await _repository.GetBasketAsync(buyerId);

        if (basket is null)
        {
            throw new RpcException(new Status(StatusCode.NotFound, $"Basket with buyer id {request.BuyerId} does not exist"));
        }

        var order = new Order()
        {
            Id = Guid.NewGuid().ToString(),
            BuyerId = buyerId,
            Items = basket.Items,
        };

        _logger.LogInformation("Checking out {Count} item(s) for BuyerId: {BuyerId}.", order.Items.Count, buyerId);

        _messageConnection ??= serviceProvider.GetService<IConnection>();
        if (_messageConnection is null)
        {
            _logger.LogWarning("RabbitMQ is unavailable. Ensure you have configured it in AppHosts's config / user secrets under 'ConnectionStrings:messaging'.");
        }
        else
        {
            const string configKeyName = "Aspire:RabbitMQ:Client:OrderQueueName";
            string? queueName = _configuration[configKeyName];
            if (string.IsNullOrEmpty(queueName))
            {
                context.Status = new Status(StatusCode.Internal, $"Queue name not found. Please add a valid name for configuration key '{configKeyName}'.");
                return new();
            }

            using var channel = _messageConnection.CreateModel();
            channel.QueueDeclare(queueName, durable: true, exclusive: false);

            var props = channel.CreateBasicProperties();
            props.Persistent = true; // or props.DeliveryMode = 2;
            channel.BasicPublish(
                exchange: "",
                routingKey: queueName,
                basicProperties: props,
                body: JsonSerializer.SerializeToUtf8Bytes(order));
        }

        await _repository.DeleteBasketAsync(buyerId);

        _logger.LogInformation("Order Id {Id} submitted.", order.Id);

        return new CheckoutCustomerBasketResponse();
    }

    public override async Task<DeleteCustomerBasketResponse> DeleteBasket(DeleteCustomerBasketRequest request, ServerCallContext context)
    {
        await _repository.DeleteBasketAsync(request.BuyerId);
        return new DeleteCustomerBasketResponse();
    }

    private static CustomerBasketResponse MapToCustomerBasketResponse(CustomerBasket customerBasket)
    {
        var response = new CustomerBasketResponse
        {
            BuyerId = customerBasket.BuyerId
        };

        foreach (var item in customerBasket.Items)
        {
            response.Items.Add(new BasketItemResponse
            {
                Id = item.Id ?? Guid.NewGuid().ToString(),
                OldUnitPrice = item.OldUnitPrice,
                ProductId = item.ProductId,
                Quantity = item.Quantity,
                UnitPrice = item.UnitPrice
            });
        }

        return response;
    }

    private static CustomerBasket MapToCustomerBasket(CustomerBasketRequest customerBasketRequest)
    {
        var response = new CustomerBasket
        {
            BuyerId = customerBasketRequest.BuyerId
        };

        foreach (var item in customerBasketRequest.Items)
        {
            response.Items.Add(new BasketItem
            {
                Id = item.Id,
                OldUnitPrice = item.OldUnitPrice,
                ProductId = item.ProductId,
                Quantity = item.Quantity,
                UnitPrice = item.UnitPrice
            });
        }

        return response;
    }
}
