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 articoli di magazzino
/// </summary>
[ApiController]
[Route("api/warehouse/articles")]
public class WarehouseArticlesController : ControllerBase
{
    private readonly IWarehouseService _warehouseService;
    private readonly ILogger<WarehouseArticlesController> _logger;

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

    /// <summary>
    /// Ottiene la lista degli articoli con filtri opzionali
    /// </summary>
    [HttpGet]
    public async Task<ActionResult<List<ArticleDto>>> GetArticles([FromQuery] ArticleFilterDto? filter)
    {
        var articleFilter = filter != null ? new ArticleFilter
        {
            SearchText = filter.Search,
            CategoryId = filter.CategoryId,
            IsActive = filter.IsActive,
            IsBatchManaged = filter.IsBatchManaged,
            IsSerialManaged = filter.IsSerialManaged,
            Skip = filter.Skip,
            Take = filter.Take,
            OrderBy = filter.OrderBy,
            OrderDescending = filter.OrderDescending
        } : null;

        var articles = await _warehouseService.GetArticlesAsync(articleFilter);
        return Ok(articles.Select(MapToDto));
    }

    /// <summary>
    /// Ottiene un articolo per ID
    /// </summary>
    [HttpGet("{id}")]
    public async Task<ActionResult<ArticleDto>> GetArticle(int id)
    {
        var article = await _warehouseService.GetArticleByIdAsync(id);
        if (article == null)
            return NotFound();

        return Ok(MapToDto(article));
    }

    /// <summary>
    /// Ottiene un articolo per codice
    /// </summary>
    [HttpGet("by-code/{code}")]
    public async Task<ActionResult<ArticleDto>> GetArticleByCode(string code)
    {
        var article = await _warehouseService.GetArticleByCodeAsync(code);
        if (article == null)
            return NotFound();

        return Ok(MapToDto(article));
    }

    /// <summary>
    /// Ottiene un articolo per barcode
    /// </summary>
    [HttpGet("by-barcode/{barcode}")]
    public async Task<ActionResult<ArticleDto>> GetArticleByBarcode(string barcode)
    {
        var article = await _warehouseService.GetArticleByBarcodeAsync(barcode);
        if (article == null)
            return NotFound();

        return Ok(MapToDto(article));
    }

    /// <summary>
    /// Crea un nuovo articolo
    /// </summary>
    [HttpPost]
    public async Task<ActionResult<ArticleDto>> CreateArticle([FromBody] CreateArticleDto dto)
    {
        try
        {
            var article = MapFromDto(dto);
            var created = await _warehouseService.CreateArticleAsync(article);
            return CreatedAtAction(nameof(GetArticle), new { id = created.Id }, MapToDto(created));
        }
        catch (InvalidOperationException ex)
        {
            return BadRequest(new { error = ex.Message });
        }
    }

    /// <summary>
    /// Aggiorna un articolo esistente
    /// </summary>
    [HttpPut("{id}")]
    public async Task<ActionResult<ArticleDto>> UpdateArticle(int id, [FromBody] UpdateArticleDto dto)
    {
        try
        {
            var existing = await _warehouseService.GetArticleByIdAsync(id);
            if (existing == null)
                return NotFound();

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

    /// <summary>
    /// Elimina un articolo
    /// </summary>
    [HttpDelete("{id}")]
    public async Task<ActionResult> DeleteArticle(int id)
    {
        try
        {
            await _warehouseService.DeleteArticleAsync(id);
            return NoContent();
        }
        catch (ArgumentException ex)
        {
            return NotFound(new { error = ex.Message });
        }
        catch (InvalidOperationException ex)
        {
            return BadRequest(new { error = ex.Message });
        }
    }

    /// <summary>
    /// Carica l'immagine di un articolo
    /// </summary>
    [HttpPost("{id}/image")]
    public async Task<ActionResult> UploadImage(int id, IFormFile file)
    {
        var article = await _warehouseService.GetArticleByIdAsync(id);
        if (article == null)
            return NotFound();

        if (file.Length > 5 * 1024 * 1024) // 5MB max
            return BadRequest(new { error = "Il file è troppo grande (max 5MB)" });

        using var memoryStream = new MemoryStream();
        await file.CopyToAsync(memoryStream);

        article.Image = memoryStream.ToArray();
        article.ImageMimeType = file.ContentType;

        await _warehouseService.UpdateArticleAsync(article);

        return Ok();
    }

    /// <summary>
    /// Ottiene l'immagine di un articolo
    /// </summary>
    [HttpGet("{id}/image")]
    public async Task<ActionResult> GetImage(int id)
    {
        var article = await _warehouseService.GetArticleByIdAsync(id);
        if (article == null || article.Image == null)
            return NotFound();

        return File(article.Image, article.ImageMimeType ?? "image/jpeg");
    }

    /// <summary>
    /// Ottiene la giacenza totale di un articolo
    /// </summary>
    [HttpGet("{id}/stock")]
    public async Task<ActionResult<ArticleStockDto>> GetArticleStock(int id)
    {
        var article = await _warehouseService.GetArticleByIdAsync(id);
        if (article == null)
            return NotFound();

        var totalStock = await _warehouseService.GetTotalStockAsync(id);
        var availableStock = await _warehouseService.GetAvailableStockAsync(id);
        var stockLevels = await _warehouseService.GetStockLevelsAsync(new StockLevelFilter { ArticleId = id });

        return Ok(new ArticleStockDto(
            ArticleId: id,
            ArticleCode: article.Code,
            ArticleDescription: article.Description,
            TotalStock: totalStock,
            AvailableStock: availableStock,
            UnitOfMeasure: article.UnitOfMeasure,
            MinimumStock: article.MinimumStock,
            MaximumStock: article.MaximumStock,
            ReorderPoint: article.ReorderPoint,
            IsLowStock: article.MinimumStock.HasValue && totalStock <= article.MinimumStock.Value,
            StockByWarehouse: stockLevels.Select(s => new WarehouseStockDto(
                WarehouseId: s.WarehouseId,
                WarehouseCode: s.Warehouse?.Code ?? "",
                WarehouseName: s.Warehouse?.Name ?? "",
                Quantity: s.Quantity,
                ReservedQuantity: s.ReservedQuantity,
                AvailableQuantity: s.AvailableQuantity,
                UnitCost: s.UnitCost,
                StockValue: s.StockValue,
                BatchId: s.BatchId,
                BatchNumber: s.Batch?.BatchNumber
            )).ToList()
        ));
    }

    #region DTOs

    public record ArticleFilterDto(
        string? Search,
        int? CategoryId,
        bool? IsActive,
        bool? IsBatchManaged,
        bool? IsSerialManaged,
        int Skip = 0,
        int Take = 100,
        string? OrderBy = null,
        bool OrderDescending = false
    );

    public record ArticleDto(
        int Id,
        string Code,
        string? AlternativeCode,
        string Description,
        string? ShortDescription,
        string? Barcode,
        string? ManufacturerCode,
        int? CategoryId,
        string? CategoryName,
        string UnitOfMeasure,
        string? SecondaryUnitOfMeasure,
        decimal? UnitConversionFactor,
        StockManagementType StockManagement,
        bool IsBatchManaged,
        bool IsSerialManaged,
        bool HasExpiry,
        int? ExpiryWarningDays,
        decimal? MinimumStock,
        decimal? MaximumStock,
        decimal? ReorderPoint,
        decimal? ReorderQuantity,
        int? LeadTimeDays,
        ValuationMethod? ValuationMethod,
        decimal? StandardCost,
        decimal? LastPurchaseCost,
        decimal? WeightedAverageCost,
        decimal? BaseSellingPrice,
        decimal? Weight,
        decimal? Volume,
        bool IsActive,
        string? Notes,
        bool HasImage,
        DateTime? CreatedAt,
        DateTime? UpdatedAt
    );

    public record CreateArticleDto(
        string Description,
        string UnitOfMeasure,
        string? AlternativeCode = null,
        string? ShortDescription = null,
        string? Barcode = null,
        string? ManufacturerCode = null,
        int? CategoryId = null,
        string? SecondaryUnitOfMeasure = null,
        decimal? UnitConversionFactor = null,
        StockManagementType StockManagement = StockManagementType.Standard,
        bool IsBatchManaged = false,
        bool IsSerialManaged = false,
        bool HasExpiry = false,
        int? ExpiryWarningDays = null,
        decimal? MinimumStock = null,
        decimal? MaximumStock = null,
        decimal? ReorderPoint = null,
        decimal? ReorderQuantity = null,
        int? LeadTimeDays = null,
        ValuationMethod? ValuationMethod = null,
        decimal? StandardCost = null,
        decimal? BaseSellingPrice = null,
        decimal? Weight = null,
        decimal? Volume = null,
        string? Notes = null
    );

    public record UpdateArticleDto(
        string Description,
        string? AlternativeCode,
        string? ShortDescription,
        string? Barcode,
        string? ManufacturerCode,
        int? CategoryId,
        string UnitOfMeasure,
        string? SecondaryUnitOfMeasure,
        decimal? UnitConversionFactor,
        StockManagementType StockManagement,
        bool IsBatchManaged,
        bool IsSerialManaged,
        bool HasExpiry,
        int? ExpiryWarningDays,
        decimal? MinimumStock,
        decimal? MaximumStock,
        decimal? ReorderPoint,
        decimal? ReorderQuantity,
        int? LeadTimeDays,
        ValuationMethod? ValuationMethod,
        decimal? StandardCost,
        decimal? BaseSellingPrice,
        decimal? Weight,
        decimal? Volume,
        bool IsActive,
        string? Notes
    );

    public record ArticleStockDto(
        int ArticleId,
        string ArticleCode,
        string ArticleDescription,
        decimal TotalStock,
        decimal AvailableStock,
        string UnitOfMeasure,
        decimal? MinimumStock,
        decimal? MaximumStock,
        decimal? ReorderPoint,
        bool IsLowStock,
        List<WarehouseStockDto> StockByWarehouse
    );

    public record WarehouseStockDto(
        int WarehouseId,
        string WarehouseCode,
        string WarehouseName,
        decimal Quantity,
        decimal ReservedQuantity,
        decimal AvailableQuantity,
        decimal? UnitCost,
        decimal? StockValue,
        int? BatchId,
        string? BatchNumber
    );

    #endregion

    #region Mapping

    private static ArticleDto MapToDto(WarehouseArticle article) => new(
        article.Id,
        article.Code,
        article.AlternativeCode,
        article.Description,
        article.ShortDescription,
        article.Barcode,
        article.ManufacturerCode,
        article.CategoryId,
        article.Category?.Name,
        article.UnitOfMeasure,
        article.SecondaryUnitOfMeasure,
        article.UnitConversionFactor,
        article.StockManagement,
        article.IsBatchManaged,
        article.IsSerialManaged,
        article.HasExpiry,
        article.ExpiryWarningDays,
        article.MinimumStock,
        article.MaximumStock,
        article.ReorderPoint,
        article.ReorderQuantity,
        article.LeadTimeDays,
        article.ValuationMethod,
        article.StandardCost,
        article.LastPurchaseCost,
        article.WeightedAverageCost,
        article.BaseSellingPrice,
        article.Weight,
        article.Volume,
        article.IsActive,
        article.Notes,
        article.Image != null,
        article.CreatedAt,
        article.UpdatedAt
    );

    private static WarehouseArticle MapFromDto(CreateArticleDto dto) => new()
    {
        // Code viene generato automaticamente da WarehouseService.CreateArticleAsync
        AlternativeCode = dto.AlternativeCode,
        Description = dto.Description,
        ShortDescription = dto.ShortDescription,
        Barcode = dto.Barcode,
        ManufacturerCode = dto.ManufacturerCode,
        CategoryId = dto.CategoryId,
        UnitOfMeasure = dto.UnitOfMeasure,
        SecondaryUnitOfMeasure = dto.SecondaryUnitOfMeasure,
        UnitConversionFactor = dto.UnitConversionFactor,
        StockManagement = dto.StockManagement,
        IsBatchManaged = dto.IsBatchManaged,
        IsSerialManaged = dto.IsSerialManaged,
        HasExpiry = dto.HasExpiry,
        ExpiryWarningDays = dto.ExpiryWarningDays,
        MinimumStock = dto.MinimumStock,
        MaximumStock = dto.MaximumStock,
        ReorderPoint = dto.ReorderPoint,
        ReorderQuantity = dto.ReorderQuantity,
        LeadTimeDays = dto.LeadTimeDays,
        ValuationMethod = dto.ValuationMethod,
        StandardCost = dto.StandardCost,
        BaseSellingPrice = dto.BaseSellingPrice,
        Weight = dto.Weight,
        Volume = dto.Volume,
        Notes = dto.Notes,
        IsActive = true
    };

    private static void UpdateFromDto(WarehouseArticle article, UpdateArticleDto dto)
    {
        // Code non viene aggiornato - è generato automaticamente e immutabile
        article.AlternativeCode = dto.AlternativeCode;
        article.Description = dto.Description;
        article.ShortDescription = dto.ShortDescription;
        article.Barcode = dto.Barcode;
        article.ManufacturerCode = dto.ManufacturerCode;
        article.CategoryId = dto.CategoryId;
        article.UnitOfMeasure = dto.UnitOfMeasure;
        article.SecondaryUnitOfMeasure = dto.SecondaryUnitOfMeasure;
        article.UnitConversionFactor = dto.UnitConversionFactor;
        article.StockManagement = dto.StockManagement;
        article.IsBatchManaged = dto.IsBatchManaged;
        article.IsSerialManaged = dto.IsSerialManaged;
        article.HasExpiry = dto.HasExpiry;
        article.ExpiryWarningDays = dto.ExpiryWarningDays;
        article.MinimumStock = dto.MinimumStock;
        article.MaximumStock = dto.MaximumStock;
        article.ReorderPoint = dto.ReorderPoint;
        article.ReorderQuantity = dto.ReorderQuantity;
        article.LeadTimeDays = dto.LeadTimeDays;
        article.ValuationMethod = dto.ValuationMethod;
        article.StandardCost = dto.StandardCost;
        article.BaseSellingPrice = dto.BaseSellingPrice;
        article.Weight = dto.Weight;
        article.Volume = dto.Volume;
        article.IsActive = dto.IsActive;
        article.Notes = dto.Notes;
    }

    #endregion
}
