using Apollinare.API.Modules.Warehouse.Services;
using Apollinare.Domain.Entities.Warehouse;
using Microsoft.AspNetCore.Mvc;

namespace Apollinare.API.Modules.Warehouse.Controllers;

/// <summary>
/// Controller per la gestione degli inventari fisici
/// </summary>
[ApiController]
[Route("api/warehouse/inventory")]
public class InventoryController : ControllerBase
{
    private readonly IWarehouseService _warehouseService;
    private readonly ILogger<InventoryController> _logger;

    public InventoryController(
        IWarehouseService warehouseService,
        ILogger<InventoryController> logger)
    {
        _warehouseService = warehouseService;
        _logger = logger;
    }

    /// <summary>
    /// Ottiene la lista degli inventari
    /// </summary>
    [HttpGet]
    public async Task<ActionResult<List<InventoryCountDto>>> GetInventoryCounts([FromQuery] InventoryStatus? status = null)
    {
        var inventories = await _warehouseService.GetInventoryCountsAsync(status);
        return Ok(inventories.Select(MapToDto));
    }

    /// <summary>
    /// Ottiene un inventario per ID
    /// </summary>
    [HttpGet("{id}")]
    public async Task<ActionResult<InventoryCountDetailDto>> GetInventoryCount(int id)
    {
        var inventory = await _warehouseService.GetInventoryCountByIdAsync(id);
        if (inventory == null)
            return NotFound();

        return Ok(MapToDetailDto(inventory));
    }

    /// <summary>
    /// Crea un nuovo inventario
    /// </summary>
    [HttpPost]
    public async Task<ActionResult<InventoryCountDto>> CreateInventoryCount([FromBody] CreateInventoryCountDto dto)
    {
        try
        {
            var inventory = new InventoryCount
            {
                Code = dto.Code ?? "",
                Description = dto.Description,
                InventoryDate = dto.InventoryDate ?? DateTime.UtcNow.Date,
                WarehouseId = dto.WarehouseId,
                CategoryId = dto.CategoryId,
                Type = dto.Type,
                Notes = dto.Notes,
                Status = InventoryStatus.Draft
            };

            var created = await _warehouseService.CreateInventoryCountAsync(inventory);
            return CreatedAtAction(nameof(GetInventoryCount), new { id = created.Id }, MapToDto(created));
        }
        catch (InvalidOperationException ex)
        {
            return BadRequest(new { error = ex.Message });
        }
    }

    /// <summary>
    /// Aggiorna un inventario esistente (solo bozze)
    /// </summary>
    [HttpPut("{id}")]
    public async Task<ActionResult<InventoryCountDto>> UpdateInventoryCount(int id, [FromBody] UpdateInventoryCountDto dto)
    {
        try
        {
            var existing = await _warehouseService.GetInventoryCountByIdAsync(id);
            if (existing == null)
                return NotFound();

            if (dto.Description != null)
                existing.Description = dto.Description;
            if (dto.InventoryDate.HasValue)
                existing.InventoryDate = dto.InventoryDate.Value;
            if (dto.WarehouseId.HasValue)
                existing.WarehouseId = dto.WarehouseId;
            if (dto.CategoryId.HasValue)
                existing.CategoryId = dto.CategoryId;
            if (dto.Type.HasValue)
                existing.Type = dto.Type.Value;
            if (dto.Notes != null)
                existing.Notes = dto.Notes;

            var updated = await _warehouseService.UpdateInventoryCountAsync(existing);
            return Ok(MapToDto(updated));
        }
        catch (InvalidOperationException ex)
        {
            return BadRequest(new { error = ex.Message });
        }
    }

    /// <summary>
    /// Avvia un inventario (genera righe da contare)
    /// </summary>
    [HttpPost("{id}/start")]
    public async Task<ActionResult<InventoryCountDetailDto>> StartInventoryCount(int id)
    {
        try
        {
            var inventory = await _warehouseService.StartInventoryCountAsync(id);
            return Ok(MapToDetailDto(inventory));
        }
        catch (ArgumentException ex)
        {
            return NotFound(new { error = ex.Message });
        }
        catch (InvalidOperationException ex)
        {
            return BadRequest(new { error = ex.Message });
        }
    }

    /// <summary>
    /// Completa un inventario (tutti i conteggi effettuati)
    /// </summary>
    [HttpPost("{id}/complete")]
    public async Task<ActionResult<InventoryCountDetailDto>> CompleteInventoryCount(int id)
    {
        try
        {
            var inventory = await _warehouseService.CompleteInventoryCountAsync(id);
            return Ok(MapToDetailDto(inventory));
        }
        catch (ArgumentException ex)
        {
            return NotFound(new { error = ex.Message });
        }
        catch (InvalidOperationException ex)
        {
            return BadRequest(new { error = ex.Message });
        }
    }

    /// <summary>
    /// Conferma un inventario (applica rettifiche)
    /// </summary>
    [HttpPost("{id}/confirm")]
    public async Task<ActionResult<InventoryCountDetailDto>> ConfirmInventoryCount(int id)
    {
        try
        {
            var inventory = await _warehouseService.ConfirmInventoryCountAsync(id);
            return Ok(MapToDetailDto(inventory));
        }
        catch (ArgumentException ex)
        {
            return NotFound(new { error = ex.Message });
        }
        catch (InvalidOperationException ex)
        {
            return BadRequest(new { error = ex.Message });
        }
    }

    /// <summary>
    /// Annulla un inventario
    /// </summary>
    [HttpPost("{id}/cancel")]
    public async Task<ActionResult<InventoryCountDto>> CancelInventoryCount(int id)
    {
        try
        {
            var inventory = await _warehouseService.CancelInventoryCountAsync(id);
            return Ok(MapToDto(inventory));
        }
        catch (ArgumentException ex)
        {
            return NotFound(new { error = ex.Message });
        }
        catch (InvalidOperationException ex)
        {
            return BadRequest(new { error = ex.Message });
        }
    }

    /// <summary>
    /// Registra il conteggio di una riga
    /// </summary>
    [HttpPut("lines/{lineId}/count")]
    public async Task<ActionResult<InventoryCountLineDto>> UpdateCountLine(int lineId, [FromBody] UpdateCountLineDto dto)
    {
        try
        {
            var line = await _warehouseService.UpdateCountLineAsync(lineId, dto.CountedQuantity, dto.CountedBy);
            return Ok(MapLineToDto(line));
        }
        catch (ArgumentException ex)
        {
            return NotFound(new { error = ex.Message });
        }
        catch (InvalidOperationException ex)
        {
            return BadRequest(new { error = ex.Message });
        }
    }

    /// <summary>
    /// Registra conteggi multipli in batch
    /// </summary>
    [HttpPut("{id}/count-batch")]
    public async Task<ActionResult> UpdateCountLinesBatch(int id, [FromBody] UpdateCountLinesBatchDto dto)
    {
        try
        {
            var results = new List<InventoryCountLineDto>();
            foreach (var lineUpdate in dto.Lines)
            {
                var line = await _warehouseService.UpdateCountLineAsync(
                    lineUpdate.LineId,
                    lineUpdate.CountedQuantity,
                    dto.CountedBy);
                results.Add(MapLineToDto(line));
            }
            return Ok(results);
        }
        catch (ArgumentException ex)
        {
            return NotFound(new { error = ex.Message });
        }
        catch (InvalidOperationException ex)
        {
            return BadRequest(new { error = ex.Message });
        }
    }

    #region DTOs

    public record InventoryCountDto(
        int Id,
        string Code,
        string Description,
        DateTime InventoryDate,
        int? WarehouseId,
        string? WarehouseCode,
        string? WarehouseName,
        int? CategoryId,
        string? CategoryName,
        InventoryType Type,
        InventoryStatus Status,
        DateTime? StartDate,
        DateTime? EndDate,
        DateTime? ConfirmedDate,
        int? AdjustmentMovementId,
        decimal? PositiveDifferenceValue,
        decimal? NegativeDifferenceValue,
        int LineCount,
        int CountedLineCount,
        string? Notes,
        DateTime? CreatedAt
    );

    public record InventoryCountDetailDto(
        int Id,
        string Code,
        string Description,
        DateTime InventoryDate,
        int? WarehouseId,
        string? WarehouseCode,
        string? WarehouseName,
        int? CategoryId,
        string? CategoryName,
        InventoryType Type,
        InventoryStatus Status,
        DateTime? StartDate,
        DateTime? EndDate,
        DateTime? ConfirmedDate,
        string? ConfirmedBy,
        int? AdjustmentMovementId,
        decimal? PositiveDifferenceValue,
        decimal? NegativeDifferenceValue,
        string? Notes,
        DateTime? CreatedAt,
        DateTime? UpdatedAt,
        List<InventoryCountLineDto> Lines
    );

    public record InventoryCountLineDto(
        int Id,
        int ArticleId,
        string ArticleCode,
        string ArticleDescription,
        int WarehouseId,
        string WarehouseCode,
        int? BatchId,
        string? BatchNumber,
        string? LocationCode,
        decimal TheoreticalQuantity,
        decimal? CountedQuantity,
        decimal? Difference,
        decimal? UnitCost,
        decimal? DifferenceValue,
        DateTime? CountedAt,
        string? CountedBy,
        decimal? SecondCountQuantity,
        string? SecondCountBy,
        string? Notes
    );

    public record CreateInventoryCountDto(
        string? Code,
        string Description,
        DateTime? InventoryDate,
        int? WarehouseId,
        int? CategoryId,
        InventoryType Type,
        string? Notes
    );

    public record UpdateInventoryCountDto(
        string? Description,
        DateTime? InventoryDate,
        int? WarehouseId,
        int? CategoryId,
        InventoryType? Type,
        string? Notes
    );

    public record UpdateCountLineDto(
        decimal CountedQuantity,
        string? CountedBy
    );

    public record UpdateCountLinesBatchDto(
        string? CountedBy,
        List<CountLineUpdate> Lines
    );

    public record CountLineUpdate(
        int LineId,
        decimal CountedQuantity
    );

    #endregion

    #region Mapping

    private static InventoryCountDto MapToDto(InventoryCount inventory) => new(
        inventory.Id,
        inventory.Code,
        inventory.Description,
        inventory.InventoryDate,
        inventory.WarehouseId,
        inventory.Warehouse?.Code,
        inventory.Warehouse?.Name,
        inventory.CategoryId,
        inventory.Category?.Name,
        inventory.Type,
        inventory.Status,
        inventory.StartDate,
        inventory.EndDate,
        inventory.ConfirmedDate,
        inventory.AdjustmentMovementId,
        inventory.PositiveDifferenceValue,
        inventory.NegativeDifferenceValue,
        inventory.Lines.Count,
        inventory.Lines.Count(l => l.CountedQuantity.HasValue),
        inventory.Notes,
        inventory.CreatedAt
    );

    private static InventoryCountDetailDto MapToDetailDto(InventoryCount inventory) => new(
        inventory.Id,
        inventory.Code,
        inventory.Description,
        inventory.InventoryDate,
        inventory.WarehouseId,
        inventory.Warehouse?.Code,
        inventory.Warehouse?.Name,
        inventory.CategoryId,
        inventory.Category?.Name,
        inventory.Type,
        inventory.Status,
        inventory.StartDate,
        inventory.EndDate,
        inventory.ConfirmedDate,
        inventory.ConfirmedBy,
        inventory.AdjustmentMovementId,
        inventory.PositiveDifferenceValue,
        inventory.NegativeDifferenceValue,
        inventory.Notes,
        inventory.CreatedAt,
        inventory.UpdatedAt,
        inventory.Lines.Select(MapLineToDto).ToList()
    );

    private static InventoryCountLineDto MapLineToDto(InventoryCountLine line) => new(
        line.Id,
        line.ArticleId,
        line.Article?.Code ?? "",
        line.Article?.Description ?? "",
        line.WarehouseId,
        line.Warehouse?.Code ?? "",
        line.BatchId,
        line.Batch?.BatchNumber,
        line.LocationCode,
        line.TheoreticalQuantity,
        line.CountedQuantity,
        line.Difference,
        line.UnitCost,
        line.DifferenceValue,
        line.CountedAt,
        line.CountedBy,
        line.SecondCountQuantity,
        line.SecondCountBy,
        line.Notes
    );

    #endregion
}
