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 dei seriali/matricole
/// </summary>
[ApiController]
[Route("api/warehouse/serials")]
public class SerialsController : ControllerBase
{
    private readonly IWarehouseService _warehouseService;
    private readonly ILogger<SerialsController> _logger;

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

    /// <summary>
    /// Ottiene la lista dei seriali con filtri opzionali
    /// </summary>
    [HttpGet]
    public async Task<ActionResult<List<SerialDto>>> GetSerials(
        [FromQuery] int? articleId = null,
        [FromQuery] SerialStatus? status = null)
    {
        var serials = await _warehouseService.GetSerialsAsync(articleId, status);
        return Ok(serials.Select(MapToDto));
    }

    /// <summary>
    /// Ottiene un seriale per ID
    /// </summary>
    [HttpGet("{id}")]
    public async Task<ActionResult<SerialDto>> GetSerial(int id)
    {
        var serial = await _warehouseService.GetSerialByIdAsync(id);
        if (serial == null)
            return NotFound();

        return Ok(MapToDto(serial));
    }

    /// <summary>
    /// Ottiene un seriale per articolo e numero seriale
    /// </summary>
    [HttpGet("by-number/{articleId}/{serialNumber}")]
    public async Task<ActionResult<SerialDto>> GetSerialByNumber(int articleId, string serialNumber)
    {
        var serial = await _warehouseService.GetSerialByNumberAsync(articleId, serialNumber);
        if (serial == null)
            return NotFound();

        return Ok(MapToDto(serial));
    }

    /// <summary>
    /// Crea un nuovo seriale
    /// </summary>
    [HttpPost]
    public async Task<ActionResult<SerialDto>> CreateSerial([FromBody] CreateSerialDto dto)
    {
        try
        {
            var serial = new ArticleSerial
            {
                ArticleId = dto.ArticleId,
                BatchId = dto.BatchId,
                SerialNumber = dto.SerialNumber,
                ManufacturerSerial = dto.ManufacturerSerial,
                ProductionDate = dto.ProductionDate,
                WarrantyExpiryDate = dto.WarrantyExpiryDate,
                CurrentWarehouseId = dto.WarehouseId,
                UnitCost = dto.UnitCost,
                SupplierId = dto.SupplierId,
                Attributes = dto.Attributes,
                Notes = dto.Notes,
                Status = SerialStatus.Available
            };

            var created = await _warehouseService.CreateSerialAsync(serial);
            return CreatedAtAction(nameof(GetSerial), new { id = created.Id }, MapToDto(created));
        }
        catch (ArgumentException ex)
        {
            return NotFound(new { error = ex.Message });
        }
        catch (InvalidOperationException ex)
        {
            return BadRequest(new { error = ex.Message });
        }
    }

    /// <summary>
    /// Crea più seriali in batch
    /// </summary>
    [HttpPost("bulk")]
    public async Task<ActionResult<List<SerialDto>>> CreateSerialsBulk([FromBody] CreateSerialsBulkDto dto)
    {
        try
        {
            var createdSerials = new List<ArticleSerial>();

            foreach (var serialNumber in dto.SerialNumbers)
            {
                var serial = new ArticleSerial
                {
                    ArticleId = dto.ArticleId,
                    BatchId = dto.BatchId,
                    SerialNumber = serialNumber,
                    ProductionDate = dto.ProductionDate,
                    WarrantyExpiryDate = dto.WarrantyExpiryDate,
                    CurrentWarehouseId = dto.WarehouseId,
                    UnitCost = dto.UnitCost,
                    SupplierId = dto.SupplierId,
                    Status = SerialStatus.Available
                };

                var created = await _warehouseService.CreateSerialAsync(serial);
                createdSerials.Add(created);
            }

            return Ok(createdSerials.Select(MapToDto));
        }
        catch (ArgumentException ex)
        {
            return NotFound(new { error = ex.Message });
        }
        catch (InvalidOperationException ex)
        {
            return BadRequest(new { error = ex.Message });
        }
    }

    /// <summary>
    /// Aggiorna un seriale esistente
    /// </summary>
    [HttpPut("{id}")]
    public async Task<ActionResult<SerialDto>> UpdateSerial(int id, [FromBody] UpdateSerialDto dto)
    {
        try
        {
            var existing = await _warehouseService.GetSerialByIdAsync(id);
            if (existing == null)
                return NotFound();

            if (dto.ManufacturerSerial != null)
                existing.ManufacturerSerial = dto.ManufacturerSerial;
            if (dto.ProductionDate.HasValue)
                existing.ProductionDate = dto.ProductionDate;
            if (dto.WarrantyExpiryDate.HasValue)
                existing.WarrantyExpiryDate = dto.WarrantyExpiryDate;
            if (dto.UnitCost.HasValue)
                existing.UnitCost = dto.UnitCost;
            if (dto.Attributes != null)
                existing.Attributes = dto.Attributes;
            if (dto.Notes != null)
                existing.Notes = dto.Notes;

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

    /// <summary>
    /// Aggiorna lo stato di un seriale
    /// </summary>
    [HttpPut("{id}/status")]
    public async Task<ActionResult> UpdateSerialStatus(int id, [FromBody] UpdateSerialStatusDto dto)
    {
        try
        {
            await _warehouseService.UpdateSerialStatusAsync(id, dto.Status);
            return Ok();
        }
        catch (ArgumentException ex)
        {
            return NotFound(new { error = ex.Message });
        }
    }

    /// <summary>
    /// Registra la vendita di un seriale
    /// </summary>
    [HttpPost("{id}/sell")]
    public async Task<ActionResult<SerialDto>> RegisterSale(int id, [FromBody] RegisterSaleDto dto)
    {
        try
        {
            var serial = await _warehouseService.GetSerialByIdAsync(id);
            if (serial == null)
                return NotFound();

            if (serial.Status != SerialStatus.Available && serial.Status != SerialStatus.Reserved)
                return BadRequest(new { error = "Il seriale non è disponibile per la vendita" });

            serial.Status = SerialStatus.Sold;
            serial.CustomerId = dto.CustomerId;
            serial.SoldDate = DateTime.UtcNow;
            serial.SalesReference = dto.SalesReference;
            serial.CurrentWarehouseId = null;

            var updated = await _warehouseService.UpdateSerialAsync(serial);
            return Ok(MapToDto(updated));
        }
        catch (ArgumentException ex)
        {
            return NotFound(new { error = ex.Message });
        }
    }

    /// <summary>
    /// Registra un reso di un seriale
    /// </summary>
    [HttpPost("{id}/return")]
    public async Task<ActionResult<SerialDto>> RegisterReturn(int id, [FromBody] RegisterReturnDto dto)
    {
        try
        {
            var serial = await _warehouseService.GetSerialByIdAsync(id);
            if (serial == null)
                return NotFound();

            if (serial.Status != SerialStatus.Sold)
                return BadRequest(new { error = "Solo i seriali venduti possono essere resi" });

            serial.Status = dto.IsDefective ? SerialStatus.Defective : SerialStatus.Returned;
            serial.CurrentWarehouseId = dto.WarehouseId;

            var updated = await _warehouseService.UpdateSerialAsync(serial);
            return Ok(MapToDto(updated));
        }
        catch (ArgumentException ex)
        {
            return NotFound(new { error = ex.Message });
        }
    }

    #region DTOs

    public record SerialDto(
        int Id,
        int ArticleId,
        string? ArticleCode,
        string? ArticleDescription,
        int? BatchId,
        string? BatchNumber,
        string SerialNumber,
        string? ManufacturerSerial,
        DateTime? ProductionDate,
        DateTime? WarrantyExpiryDate,
        int? CurrentWarehouseId,
        string? CurrentWarehouseCode,
        string? CurrentWarehouseName,
        SerialStatus Status,
        decimal? UnitCost,
        int? SupplierId,
        int? CustomerId,
        DateTime? SoldDate,
        string? SalesReference,
        string? Attributes,
        string? Notes,
        bool IsWarrantyValid,
        int? DaysToWarrantyExpiry,
        DateTime? CreatedAt,
        DateTime? UpdatedAt
    );

    public record CreateSerialDto(
        int ArticleId,
        int? BatchId,
        string SerialNumber,
        string? ManufacturerSerial,
        DateTime? ProductionDate,
        DateTime? WarrantyExpiryDate,
        int? WarehouseId,
        decimal? UnitCost,
        int? SupplierId,
        string? Attributes,
        string? Notes
    );

    public record CreateSerialsBulkDto(
        int ArticleId,
        int? BatchId,
        List<string> SerialNumbers,
        DateTime? ProductionDate,
        DateTime? WarrantyExpiryDate,
        int? WarehouseId,
        decimal? UnitCost,
        int? SupplierId
    );

    public record UpdateSerialDto(
        string? ManufacturerSerial,
        DateTime? ProductionDate,
        DateTime? WarrantyExpiryDate,
        decimal? UnitCost,
        string? Attributes,
        string? Notes
    );

    public record UpdateSerialStatusDto(SerialStatus Status);

    public record RegisterSaleDto(
        int? CustomerId,
        string? SalesReference
    );

    public record RegisterReturnDto(
        int WarehouseId,
        bool IsDefective
    );

    #endregion

    #region Mapping

    private static SerialDto MapToDto(ArticleSerial serial)
    {
        var isWarrantyValid = serial.WarrantyExpiryDate.HasValue && serial.WarrantyExpiryDate.Value > DateTime.UtcNow;
        var daysToWarrantyExpiry = serial.WarrantyExpiryDate.HasValue
            ? (int?)Math.Max(0, (serial.WarrantyExpiryDate.Value - DateTime.UtcNow).Days)
            : null;

        return new SerialDto(
            serial.Id,
            serial.ArticleId,
            serial.Article?.Code,
            serial.Article?.Description,
            serial.BatchId,
            serial.Batch?.BatchNumber,
            serial.SerialNumber,
            serial.ManufacturerSerial,
            serial.ProductionDate,
            serial.WarrantyExpiryDate,
            serial.CurrentWarehouseId,
            serial.CurrentWarehouse?.Code,
            serial.CurrentWarehouse?.Name,
            serial.Status,
            serial.UnitCost,
            serial.SupplierId,
            serial.CustomerId,
            serial.SoldDate,
            serial.SalesReference,
            serial.Attributes,
            serial.Notes,
            isWarrantyValid,
            daysToWarrantyExpiry,
            serial.CreatedAt,
            serial.UpdatedAt
        );
    }

    #endregion
}
