// EN: Main controller for Inventory operations.
// VI: Controller chính cho các thao tác Inventory.

using InventoryService.API.Application.Commands;
using InventoryService.API.Application.Commands.DeductInventory;
using InventoryService.API.Application.DTOs;
using InventoryService.API.Application.Queries;
using MediatR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Swashbuckle.AspNetCore.Annotations;

namespace InventoryService.API.Controllers;

/// <summary>
/// EN: Controller for inventory operations.
/// VI: Controller cho các thao tác inventory.
/// </summary>
[ApiController]
[Route("api/v1/inventory")]
[SwaggerTag("Inventory Management - Stock operations, reservations, and tracking")]
[Authorize]
public class InventoryController : ControllerBase
{
    private readonly IMediator _mediator;
    private readonly ILogger<InventoryController> _logger;

    public InventoryController(
        IMediator mediator,
        ILogger<InventoryController> logger)
    {
        _mediator = mediator;
        _logger = logger;
    }

    /// <summary>
    /// EN: Get inventory by shop.
    /// VI: Lấy inventory theo shop.
    /// </summary>
    [HttpGet]
    [SwaggerOperation(Summary = "Get inventory by shop with pagination")]
    [SwaggerResponse(200, "Inventory items retrieved successfully")]
    [SwaggerResponse(400, "Invalid request")]
    public async Task<ActionResult<ApiResponse<PagedResult<InventoryItemDto>>>> GetInventory(
        [FromQuery] Guid shopId,
        [FromQuery] int skip = 0,
        [FromQuery] int take = 50,
        CancellationToken ct = default)
    {
        if (shopId == Guid.Empty)
            return BadRequest(ApiResponse<PagedResult<InventoryItemDto>>.Fail("Shop ID is required"));

        var query = new GetInventoryByShopQuery(shopId, skip, take);
        var result = await _mediator.Send(query, ct);

        return Ok(ApiResponse<PagedResult<InventoryItemDto>>.Ok(result));
    }

    /// <summary>
    /// EN: Get stock level for specific product and shop.
    /// VI: Lấy mức tồn kho cho product và shop cụ thể.
    /// </summary>
    [HttpGet("{productId:guid}")]
    [SwaggerOperation(Summary = "Get stock level by product ID and shop ID")]
    [SwaggerResponse(200, "Stock level retrieved successfully")]
    [SwaggerResponse(404, "Inventory item not found")]
    public async Task<ActionResult<ApiResponse<InventoryItemDto>>> GetStockLevel(
        Guid productId,
        [FromQuery] Guid shopId,
        CancellationToken ct = default)
    {
        var query = new GetStockLevelQuery(productId, shopId);
        var result = await _mediator.Send(query, ct);

        if (result == null)
            return NotFound(ApiResponse<InventoryItemDto>.Fail("Inventory item not found"));

        return Ok(ApiResponse<InventoryItemDto>.Ok(result));
    }

    /// <summary>
    /// EN: Bulk stock check for POS cart validation.
    /// VI: Kiểm tra stock hàng loạt cho xác thực giỏ hàng POS.
    /// </summary>
    [HttpPost("stock/bulk")]
    [SwaggerOperation(Summary = "Bulk stock check for multiple products (POS cart validation)")]
    [SwaggerResponse(200, "Stock levels retrieved successfully")]
    [SwaggerResponse(400, "Invalid request")]
    public async Task<ActionResult<ApiResponse<IReadOnlyList<StockLevelDto>>>> BulkStockCheck(
        [FromBody] BulkStockCheckRequest request,
        CancellationToken ct = default)
    {
        if (request.ShopId == Guid.Empty)
            return BadRequest(ApiResponse<IReadOnlyList<StockLevelDto>>.Fail("Shop ID is required / Shop ID là bắt buộc"));

        if (request.ProductIds == null || request.ProductIds.Count == 0)
            return BadRequest(ApiResponse<IReadOnlyList<StockLevelDto>>.Fail("At least one product ID is required / Cần ít nhất một product ID"));

        var query = new GetStockLevelsQuery(request.ShopId, request.ProductIds);
        var result = await _mediator.Send(query, ct);

        return Ok(ApiResponse<IReadOnlyList<StockLevelDto>>.Ok(result));
    }

    /// <summary>
    /// EN: Set low stock alert threshold for a product.
    /// VI: Cài đặt ngưỡng cảnh báo stock thấp cho sản phẩm.
    /// </summary>
    [HttpPut("alerts")]
    [SwaggerOperation(Summary = "Set low stock alert threshold")]
    [SwaggerResponse(200, "Alert threshold set successfully")]
    [SwaggerResponse(400, "Invalid request")]
    [SwaggerResponse(404, "Inventory item not found")]
    public async Task<ActionResult<ApiResponse<bool>>> SetLowStockAlert(
        [FromBody] SetLowStockAlertRequest request,
        CancellationToken ct = default)
    {
        try
        {
            var command = new SetLowStockAlertCommand(
                request.ShopId,
                request.ProductId,
                request.MinimumLevel);

            var result = await _mediator.Send(command, ct);

            if (!result)
                return NotFound(ApiResponse<bool>.Fail("Inventory item not found / Không tìm thấy inventory item"));

            return Ok(ApiResponse<bool>.Ok(true));
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "EN: Error setting low stock alert / VI: Lỗi cài đặt cảnh báo stock thấp");
            return BadRequest(ApiResponse<bool>.Fail(ex.Message));
        }
    }

    /// <summary>
    /// EN: Create a new inventory item (raw material, consumable, etc.).
    /// VI: Tạo mặt hàng tồn kho mới (nguyên liệu, vật tư tiêu hao, v.v.).
    /// </summary>
    [HttpPost("items")]
    [SwaggerOperation(Summary = "Create a new inventory item")]
    [SwaggerResponse(201, "Item created successfully")]
    [SwaggerResponse(400, "Invalid request")]
    public async Task<ActionResult<ApiResponse<Guid>>> CreateItem(
        [FromBody] CreateInventoryItemRequest request,
        CancellationToken ct = default)
    {
        try
        {
            var command = new CreateInventoryItemCommand(
                request.ShopId,
                request.Name,
                request.ItemTypeId,
                request.Unit,
                request.CostPerUnit,
                request.InitialQuantity,
                request.ReorderLevel,
                request.SupplierName,
                request.ExpiryDate);

            var itemId = await _mediator.Send(command, ct);
            return Created($"/api/v1/inventory/{itemId}", ApiResponse<Guid>.Ok(itemId));
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error creating inventory item");
            return BadRequest(ApiResponse<Guid>.Fail(ex.Message));
        }
    }

    /// <summary>
    /// EN: Stock in operation (add inventory).
    /// VI: Thao tác nhập kho (thêm inventory).
    /// </summary>
    [HttpPost("stock-in")]
    [SwaggerOperation(Summary = "Add stock to inventory")]
    [SwaggerResponse(200, "Stock added successfully")]
    [SwaggerResponse(400, "Invalid request")]
    public async Task<ActionResult<ApiResponse<Guid>>> StockIn(
        [FromBody] StockInRequest request,
        CancellationToken ct = default)
    {
        try
        {
            var command = new StockInCommand(
                request.ProductId,
                request.ShopId,
                request.Amount,
                request.Notes,
                request.ReferenceId,
                request.InvoiceImageUrl,
                request.UnitCost);

            var inventoryItemId = await _mediator.Send(command, ct);

            return Ok(ApiResponse<Guid>.Ok(inventoryItemId));
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error performing stock in");
            return BadRequest(ApiResponse<Guid>.Fail(ex.Message));
        }
    }

    /// <summary>
    /// EN: Stock out operation (remove inventory).
    /// VI: Thao tác xuất kho (giảm inventory).
    /// </summary>
    [HttpPost("stock-out")]
    [SwaggerOperation(Summary = "Remove stock from inventory")]
    [SwaggerResponse(200, "Stock removed successfully")]
    [SwaggerResponse(400, "Invalid request or insufficient stock")]
    [SwaggerResponse(404, "Inventory item not found")]
    public async Task<ActionResult<ApiResponse<bool>>> StockOut(
        [FromBody] StockOutRequest request,
        CancellationToken ct = default)
    {
        try
        {
            var command = new StockOutCommand(
                request.ProductId,
                request.ShopId,
                request.Amount,
                request.Notes,
                request.ReferenceId);

            var result = await _mediator.Send(command, ct);

            if (!result)
                return NotFound(ApiResponse<bool>.Fail("Inventory item not found"));

            return Ok(ApiResponse<bool>.Ok(result));
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error performing stock out");
            return BadRequest(ApiResponse<bool>.Fail(ex.Message));
        }
    }

    /// <summary>
    /// EN: Stock out by inventory item ID (for recipe ingredient deduction).
    /// VI: Xuất kho theo inventory item ID (cho trừ nguyên liệu công thức).
    /// </summary>
    [HttpPost("stock-out-by-id")]
    [SwaggerOperation(Summary = "Deduct stock by inventory item ID")]
    [SwaggerResponse(200, "Stock deducted successfully")]
    [SwaggerResponse(400, "Invalid request or insufficient stock")]
    [SwaggerResponse(404, "Inventory item not found")]
    public async Task<ActionResult<ApiResponse<bool>>> StockOutById(
        [FromBody] StockOutByIdRequest request,
        CancellationToken ct = default)
    {
        try
        {
            var command = new StockOutByIdCommand(
                request.InventoryItemId,
                request.Amount,
                request.Notes,
                request.ReferenceId);

            var result = await _mediator.Send(command, ct);
            if (!result)
                return NotFound(ApiResponse<bool>.Fail("Inventory item not found"));

            return Ok(ApiResponse<bool>.Ok(result));
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error performing stock out by ID");
            return BadRequest(ApiResponse<bool>.Fail(ex.Message));
        }
    }

    /// <summary>
    /// EN: Reserve stock for order.
    /// VI: Đặt trước stock cho order.
    /// </summary>
    [HttpPost("reserve")]
    [SwaggerOperation(Summary = "Reserve stock for order")]
    [SwaggerResponse(200, "Stock reserved successfully")]
    [SwaggerResponse(400, "Invalid request or insufficient available stock")]
    [SwaggerResponse(404, "Inventory item not found")]
    public async Task<ActionResult<ApiResponse<bool>>> ReserveStock(
        [FromBody] ReserveStockRequest request,
        CancellationToken ct = default)
    {
        try
        {
            var command = new ReserveStockCommand(
                request.ProductId,
                request.ShopId,
                request.Amount,
                request.OrderId);

            var result = await _mediator.Send(command, ct);

            if (!result)
                return NotFound(ApiResponse<bool>.Fail("Inventory item not found"));

            return Ok(ApiResponse<bool>.Ok(result));
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error reserving stock");
            return BadRequest(ApiResponse<bool>.Fail(ex.Message));
        }
    }

    /// <summary>
    /// EN: Release stock reservation.
    /// VI: Giải phóng đặt trước stock.
    /// </summary>
    [HttpPost("release")]
    [SwaggerOperation(Summary = "Release stock reservation")]
    [SwaggerResponse(200, "Reservation released successfully")]
    [SwaggerResponse(400, "Invalid request")]
    [SwaggerResponse(404, "Inventory item not found")]
    public async Task<ActionResult<ApiResponse<bool>>> ReleaseReservation(
        [FromBody] ReleaseReservationRequest request,
        CancellationToken ct = default)
    {
        try
        {
            var command = new ReleaseReservationCommand(
                request.ProductId,
                request.ShopId,
                request.Amount,
                request.OrderId);

            var result = await _mediator.Send(command, ct);

            if (!result)
                return NotFound(ApiResponse<bool>.Fail("Inventory item not found"));

            return Ok(ApiResponse<bool>.Ok(result));
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error releasing reservation");
            return BadRequest(ApiResponse<bool>.Fail(ex.Message));
        }
    }

    /// <summary>
    /// EN: Adjust stock (manual correction).
    /// VI: Điều chỉnh stock (sửa thủ công).
    /// </summary>
    [HttpPost("adjust")]
    [SwaggerOperation(Summary = "Manually adjust stock quantity")]
    [SwaggerResponse(200, "Stock adjusted successfully")]
    [SwaggerResponse(400, "Invalid request")]
    [SwaggerResponse(404, "Inventory item not found")]
    public async Task<ActionResult<ApiResponse<bool>>> AdjustStock(
        [FromBody] AdjustStockRequest request,
        CancellationToken ct = default)
    {
        try
        {
            var command = new AdjustStockCommand(
                request.ProductId,
                request.ShopId,
                request.NewQuantity,
                request.Notes);

            var result = await _mediator.Send(command, ct);

            if (!result)
                return NotFound(ApiResponse<bool>.Fail("Inventory item not found"));

            return Ok(ApiResponse<bool>.Ok(result));
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error adjusting stock");
            return BadRequest(ApiResponse<bool>.Fail(ex.Message));
        }
    }

    /// <summary>
    /// EN: Record wastage/shrinkage.
    /// VI: Ghi nhận hao hụt.
    /// </summary>
    [HttpPost("wastage")]
    [SwaggerOperation(Summary = "Record wastage/shrinkage for an inventory item")]
    [SwaggerResponse(200, "Wastage recorded successfully")]
    [SwaggerResponse(400, "Invalid request")]
    [SwaggerResponse(404, "Inventory item not found")]
    public async Task<ActionResult<ApiResponse<bool>>> RecordWastage(
        [FromBody] RecordWastageRequest request,
        CancellationToken ct = default)
    {
        try
        {
            var command = new RecordWastageCommand(
                request.InventoryItemId,
                request.Amount,
                request.Reason,
                request.Notes);

            var result = await _mediator.Send(command, ct);

            if (!result)
                return NotFound(ApiResponse<bool>.Fail("Inventory item not found"));

            return Ok(ApiResponse<bool>.Ok(result));
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error recording wastage");
            return BadRequest(ApiResponse<bool>.Fail(ex.Message));
        }
    }

    /// <summary>
    /// EN: Perform stocktake (inventory count).
    /// VI: Thực hiện kiểm kê (đếm tồn kho).
    /// </summary>
    [HttpPost("stocktake")]
    [SwaggerOperation(Summary = "Perform stocktake and return discrepancies")]
    [SwaggerResponse(200, "Stocktake completed successfully")]
    [SwaggerResponse(400, "Invalid request")]
    public async Task<ActionResult<ApiResponse<StocktakeResult>>> Stocktake(
        [FromBody] StocktakeRequest request,
        CancellationToken ct = default)
    {
        try
        {
            var command = new StocktakeCommand(
                request.ShopId,
                request.Items.Select(i => new StocktakeItem(i.InventoryItemId, i.CountedQuantity)).ToList());

            var result = await _mediator.Send(command, ct);

            return Ok(ApiResponse<StocktakeResult>.Ok(result));
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error performing stocktake");
            return BadRequest(ApiResponse<StocktakeResult>.Fail(ex.Message));
        }
    }

    /// <summary>
    /// EN: Delete an inventory item.
    /// VI: Xóa nguyên liệu khỏi tồn kho.
    /// </summary>
    [HttpDelete("items/{id:guid}")]
    [SwaggerOperation(Summary = "Delete an inventory item")]
    [SwaggerResponse(200, "Item deleted successfully")]
    [SwaggerResponse(404, "Item not found")]
    public async Task<ActionResult<ApiResponse<bool>>> DeleteItem(
        Guid id,
        CancellationToken ct = default)
    {
        try
        {
            var result = await _mediator.Send(new DeleteInventoryItemCommand(id), ct);
            if (!result)
                return NotFound(ApiResponse<bool>.Fail("Inventory item not found"));
            return Ok(ApiResponse<bool>.Ok(true));
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error deleting inventory item");
            return BadRequest(ApiResponse<bool>.Fail(ex.Message));
        }
    }

    /// <summary>
    /// EN: Get transaction history by inventory item ID or shop ID.
    /// VI: Lấy lịch sử transactions theo inventory item ID hoặc shop ID.
    /// </summary>
    [HttpGet("transactions")]
    [SwaggerOperation(Summary = "Get transaction history by item or shop")]
    [SwaggerResponse(200, "Transactions retrieved successfully")]
    [SwaggerResponse(400, "Invalid request")]
    public async Task<ActionResult<ApiResponse<PagedResult<InventoryTransactionDto>>>> GetTransactions(
        [FromQuery] Guid? inventoryItemId = null,
        [FromQuery] Guid? shopId = null,
        [FromQuery] int skip = 0,
        [FromQuery] int take = 50,
        CancellationToken ct = default)
    {
        if (shopId.HasValue && shopId.Value != Guid.Empty)
        {
            var shopQuery = new GetTransactionsByShopQuery(shopId.Value, skip, take);
            var shopResult = await _mediator.Send(shopQuery, ct);
            return Ok(ApiResponse<PagedResult<InventoryTransactionDto>>.Ok(shopResult));
        }

        if (!inventoryItemId.HasValue || inventoryItemId.Value == Guid.Empty)
            return BadRequest(ApiResponse<PagedResult<InventoryTransactionDto>>.Fail("Either shopId or inventoryItemId is required"));

        var query = new GetTransactionsQuery(inventoryItemId.Value, skip, take);
        var result = await _mediator.Send(query, ct);

        return Ok(ApiResponse<PagedResult<InventoryTransactionDto>>.Ok(result));
    }

    /// <summary>
    /// EN: Bulk deduct inventory items (called by fnb-engine when kitchen ticket is served).
    /// VI: Trừ kho hàng loạt (được gọi bởi fnb-engine khi phiếu bếp đã phục vụ).
    /// </summary>
    [HttpPost("deduct")]
    [SwaggerOperation(Summary = "Bulk deduct inventory items (cross-service: kitchen ticket served)")]
    [SwaggerResponse(200, "Deduction processed")]
    [SwaggerResponse(400, "Invalid request")]
    public async Task<ActionResult<ApiResponse<DeductInventoryResult>>> DeductInventory(
        [FromBody] DeductInventoryRequest request,
        CancellationToken ct = default)
    {
        try
        {
            var command = new DeductInventoryCommand(
                request.ShopId,
                request.ReferenceId,
                request.ReferenceType,
                request.Reason ?? "Kitchen ticket deduction",
                request.Items.Select(i => new DeductionItem(
                    i.InventoryItemId, i.Amount, i.Unit, i.IngredientName)).ToList());

            var result = await _mediator.Send(command, ct);

            return Ok(ApiResponse<DeductInventoryResult>.Ok(result));
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "EN: Error processing inventory deduction / VI: Lỗi xử lý trừ kho");
            return BadRequest(ApiResponse<DeductInventoryResult>.Fail(ex.Message));
        }
    }

    /// <summary>
    /// EN: Get low stock items.
    /// VI: Lấy các items stock thấp.
    /// </summary>
    [HttpGet("low-stock")]
    [SwaggerOperation(Summary = "Get items with stock at or below reorder level")]
    [SwaggerResponse(200, "Low stock items retrieved successfully")]
    public async Task<ActionResult<ApiResponse<PagedResult<InventoryItemDto>>>> GetLowStockItems(
        [FromQuery] Guid? shopId = null,
        [FromQuery] int skip = 0,
        [FromQuery] int take = 50,
        CancellationToken ct = default)
    {
        var query = new GetLowStockItemsQuery(shopId, skip, take);
        var result = await _mediator.Send(query, ct);

        return Ok(ApiResponse<PagedResult<InventoryItemDto>>.Ok(result));
    }
}
