using CMSMicroservice.Protobuf.Protos.Inventory;
using CMSMicroservice.WebApi.Common.Services;
using CMSMicroservice.Application.WarehouseCQ.Commands.CreateWarehouse;
using CMSMicroservice.Application.WarehouseCQ.Commands.UpdateWarehouse;
using CMSMicroservice.Application.WarehouseCQ.Commands.DeleteWarehouse;
using CMSMicroservice.Application.WarehouseCQ.Commands.SetDefaultWarehouse;
using CMSMicroservice.Application.WarehouseCQ.Queries.GetWarehouse;
using CMSMicroservice.Application.WarehouseCQ.Queries.GetAllWarehouses;
using CMSMicroservice.Application.InventoryItemCQ.Commands.UpdateInventoryItem;
using CMSMicroservice.Application.InventoryItemCQ.Commands.IncreaseInventory;
using CMSMicroservice.Application.InventoryItemCQ.Commands.ReduceInventory;
using CMSMicroservice.Application.InventoryItemCQ.Commands.ReserveInventory;
using CMSMicroservice.Application.InventoryItemCQ.Commands.ReleaseReservedInventory;
using CMSMicroservice.Application.InventoryItemCQ.Queries.GetInventoryItem;
using CMSMicroservice.Application.InventoryItemCQ.Queries.GetInventoryByProduct;
using CMSMicroservice.Application.InventoryItemCQ.Queries.GetAllInventoryItems;
using CMSMicroservice.Application.InventoryItemCQ.Queries.GetLowStockItems;
using CMSMicroservice.Application.StockMovementCQ.Commands.CreateStockMovement;
using CMSMicroservice.Application.StockMovementCQ.Queries.GetStockMovements;
using CMSMicroservice.Application.StockMovementCQ.Queries.GetStockMovementsByInventoryItem;
using Google.Protobuf.WellKnownTypes;
using Grpc.Core;
using MediatR;

namespace CMSMicroservice.WebApi.Services;

public class InventoryService : InventoryContract.InventoryContractBase
{
    private readonly IDispatchRequestToCQRS _dispatchRequestToCQRS;
    private readonly IMediator _mediator;

    public InventoryService(IDispatchRequestToCQRS dispatchRequestToCQRS, IMediator mediator)
    {
        _dispatchRequestToCQRS = dispatchRequestToCQRS;
        _mediator = mediator;
    }

    // ========== Warehouse Management ==========

    public override async Task<CreateWarehouseResponse> CreateWarehouse(CreateWarehouseRequest request, ServerCallContext context)
    {
        return await _dispatchRequestToCQRS.Handle<CreateWarehouseRequest, CreateWarehouseCommand, CreateWarehouseResponse>(request, context);
    }

    public override async Task<Empty> UpdateWarehouse(UpdateWarehouseRequest request, ServerCallContext context)
    {
        return await _dispatchRequestToCQRS.Handle<UpdateWarehouseRequest, UpdateWarehouseCommand>(request, context);
    }

    public override async Task<Empty> DeleteWarehouse(DeleteWarehouseRequest request, ServerCallContext context)
    {
        return await _dispatchRequestToCQRS.Handle<DeleteWarehouseRequest, DeleteWarehouseCommand>(request, context);
    }

    public override async Task<GetWarehouseResponse> GetWarehouse(GetWarehouseRequest request, ServerCallContext context)
    {
        return await _dispatchRequestToCQRS.Handle<GetWarehouseRequest, GetWarehouseQuery, GetWarehouseResponse>(request, context);
    }

    public override async Task<GetAllWarehousesResponse> GetAllWarehouses(GetAllWarehousesRequest request, ServerCallContext context)
    {
        return await _dispatchRequestToCQRS.Handle<GetAllWarehousesRequest, GetAllWarehousesQuery, GetAllWarehousesResponse>(request, context);
    }

    public override async Task<Empty> SetDefaultWarehouse(SetDefaultWarehouseRequest request, ServerCallContext context)
    {
        return await _dispatchRequestToCQRS.Handle<SetDefaultWarehouseRequest, SetDefaultWarehouseCommand>(request, context);
    }

    // ========== Inventory Item Management ==========

    public override async Task<GetInventoryItemResponse> GetInventoryItem(GetInventoryItemRequest request, ServerCallContext context)
    {
        return await _dispatchRequestToCQRS.Handle<GetInventoryItemRequest, GetInventoryItemQuery, GetInventoryItemResponse>(request, context);
    }

    public override async Task<GetInventoryByProductResponse> GetInventoryByProduct(GetInventoryByProductRequest request, ServerCallContext context)
    {
        return await _dispatchRequestToCQRS.Handle<GetInventoryByProductRequest, GetInventoryByProductQuery, GetInventoryByProductResponse>(request, context);
    }

    public override async Task<GetAllInventoryItemsResponse> GetAllInventoryItems(GetAllInventoryItemsRequest request, ServerCallContext context)
    {
        return await _dispatchRequestToCQRS.Handle<GetAllInventoryItemsRequest, GetAllInventoryItemsQuery, GetAllInventoryItemsResponse>(request, context);
    }

    public override async Task<GetLowStockItemsResponse> GetLowStockItems(GetLowStockItemsRequest request, ServerCallContext context)
    {
        return await _dispatchRequestToCQRS.Handle<GetLowStockItemsRequest, GetLowStockItemsQuery, GetLowStockItemsResponse>(request, context);
    }

    public override async Task<Empty> UpdateInventorySettings(UpdateInventorySettingsRequest request, ServerCallContext context)
    {
        return await _dispatchRequestToCQRS.Handle<UpdateInventorySettingsRequest, UpdateInventoryItemCommand>(request, context);
    }

    // ========== Stock Operations ==========

    public override async Task<AddStockResponse> AddStock(AddStockRequest request, ServerCallContext context)
    {
        // Lookup InventoryItem by ProductId + ProductType
        var inventoryItem = await _mediator.Send(
            new GetInventoryByProductQuery
            {
                ProductId = request.ProductId,
                ProductType = (Domain.Enums.ProductType)request.ProductType
            },
            context.CancellationToken);

        if (inventoryItem == null)
        {
            throw new RpcException(new Status(StatusCode.NotFound, 
                $"Inventory item not found for ProductId={request.ProductId}, ProductType={request.ProductType}"));
        }

        // Execute IncreaseInventoryCommand
        var response = await _mediator.Send(
            new IncreaseInventoryCommand
            {
                Id = inventoryItem.Id,
                Quantity = request.Quantity,
                ReferenceNumber = request.ReferenceNumber
            },
            context.CancellationToken);

        return new AddStockResponse
        {
            InventoryItemId = inventoryItem.Id,
            NewQuantity = response.NewQuantity
        };
    }

    public override async Task<AdjustStockResponse> AdjustStock(AdjustStockRequest request, ServerCallContext context)
    {
        // Lookup InventoryItem
        var inventoryItem = await _mediator.Send(
            new GetInventoryByProductQuery
            {
                ProductId = request.ProductId,
                ProductType = (Domain.Enums.ProductType)request.ProductType
            },
            context.CancellationToken);

        if (inventoryItem == null)
        {
            throw new RpcException(new Status(StatusCode.NotFound, 
                $"Inventory item not found for ProductId={request.ProductId}, ProductType={request.ProductType}"));
        }

        // Determine if increase or decrease
        var difference = request.NewQuantity - inventoryItem.Quantity;

        if (difference > 0)
        {
            await _mediator.Send(
                new IncreaseInventoryCommand
                {
                    Id = inventoryItem.Id,
                    Quantity = difference,
                    ReferenceNumber = request.ReferenceNumber
                },
                context.CancellationToken);

            return new AdjustStockResponse
            {
                PreviousQuantity = inventoryItem.Quantity,
                NewQuantity = request.NewQuantity,
                Difference = difference
            };
        }
        else if (difference < 0)
        {
            await _mediator.Send(
                new ReduceInventoryCommand
                {
                    Id = inventoryItem.Id,
                    Quantity = Math.Abs(difference),
                    FromReserved = false,
                    ReferenceNumber = request.ReferenceNumber
                },
                context.CancellationToken);

            return new AdjustStockResponse
            {
                PreviousQuantity = inventoryItem.Quantity,
                NewQuantity = request.NewQuantity,
                Difference = difference
            };
        }
        else
        {
            return new AdjustStockResponse
            {
                PreviousQuantity = inventoryItem.Quantity,
                NewQuantity = inventoryItem.Quantity,
                Difference = 0
            };
        }
    }

    public override Task<ReserveStockResponse> ReserveStock(ReserveStockRequest request, ServerCallContext context)
    {
        // TODO: Implement with product lookup
        throw new RpcException(new Status(StatusCode.Unimplemented, "ReserveStock requires product lookup - not yet implemented"));
    }

    public override Task<Empty> ReleaseReservation(ReleaseReservationRequest request, ServerCallContext context)
    {
        // TODO: Implement with product lookup
        throw new RpcException(new Status(StatusCode.Unimplemented, "ReleaseReservation requires product lookup - not yet implemented"));
    }

    public override Task<Empty> ConfirmSale(ConfirmSaleRequest request, ServerCallContext context)
    {
        // TODO: Implement with product lookup
        throw new RpcException(new Status(StatusCode.Unimplemented, "ConfirmSale requires product lookup - not yet implemented"));
    }

    public override Task<ProcessReturnResponse> ProcessReturn(ProcessReturnRequest request, ServerCallContext context)
    {
        // TODO: Implement with product lookup
        throw new RpcException(new Status(StatusCode.Unimplemented, "ProcessReturn requires product lookup - not yet implemented"));
    }

    public override async Task<Empty> RecordLoss(RecordLossRequest request, ServerCallContext context)
    {
        // Lookup InventoryItem by ProductId + ProductType
        var inventoryItem = await _mediator.Send(
            new GetInventoryByProductQuery
            {
                ProductId = request.ProductId,
                ProductType = (Domain.Enums.ProductType)request.ProductType
            },
            context.CancellationToken);

        if (inventoryItem == null)
        {
            throw new RpcException(new Status(StatusCode.NotFound, 
                $"Inventory item not found for ProductId={request.ProductId}, ProductType={request.ProductType}"));
        }

        // Execute ReduceInventoryCommand to record the loss
        await _mediator.Send(
            new ReduceInventoryCommand
            {
                Id = inventoryItem.Id,
                Quantity = request.Quantity,
                FromReserved = false,
                ReferenceNumber = request.ReferenceNumber ?? $"LOSS-{DateTime.UtcNow:yyyyMMddHHmmss}"
            },
            context.CancellationToken);

        return new Empty();
    }

    // ========== Bulk Operations ==========

    public override Task<BulkAddStockResponse> BulkAddStock(BulkAddStockRequest request, ServerCallContext context)
    {
        // TODO: Implement with product lookup
        throw new RpcException(new Status(StatusCode.Unimplemented, "BulkAddStock requires product lookup - not yet implemented"));
    }

    // ========== Stock Movements ==========

    public override async Task<GetStockMovementsResponse> GetStockMovements(GetStockMovementsRequest request, ServerCallContext context)
    {
        return await _dispatchRequestToCQRS.Handle<GetStockMovementsRequest, GetStockMovementsQuery, GetStockMovementsResponse>(request, context);
    }

    public override async Task<GetStockMovementsByInventoryItemResponse> GetStockMovementsByInventoryItem(GetStockMovementsByInventoryItemRequest request, ServerCallContext context)
    {
        return await _dispatchRequestToCQRS.Handle<GetStockMovementsByInventoryItemRequest, GetStockMovementsByInventoryItemQuery, GetStockMovementsByInventoryItemResponse>(request, context);
    }

    // ========== Reports ==========

    public override Task<GetInventorySummaryResponse> GetInventorySummary(GetInventorySummaryRequest request, ServerCallContext context)
    {
        // TODO: Implement summary query
        return Task.FromResult(new GetInventorySummaryResponse
        {
            TotalProducts = 0,
            TotalDiscountProducts = 0,
            TotalQuantity = 0,
            TotalReserved = 0,
            LowStockCount = 0,
            OutOfStockCount = 0,
            TotalStockValue = 0
        });
    }

    public override Task<GetStockValueReportResponse> GetStockValueReport(GetStockValueReportRequest request, ServerCallContext context)
    {
        // TODO: Implement stock value report query
        return Task.FromResult(new GetStockValueReportResponse
        {
            TotalValue = 0,
            TotalItems = 0
        });
    }
}
