@using Blazored.Modal
@using Blazored.Modal.Services
@using Domain.Dtos.Stock
@using Microsoft.AspNetCore.Components.Web
@using System.Globalization

@inject IToastService toastService
@inject IStockScanService stockScanService
@inject IModalService Modal

@inherits LayoutComponentBase

<div class="modal-header bg-dark text-white">
    <h5 class="modal-title">Seleccionar artículos de stock</h5>
    <button type="button" class="btn-close" aria-label="Close" @onclick="Cancel"></button>
</div>

<div class="modal-body" style="zoom:0.9;">
    <div class="mb-3">
        <label for="scan" class="form-label">Escanear o ingresar código</label>
        <div class="input-group">
            <span class="input-group-text">
                <i class="fas fa-qrcode"></i>
            </span>

            <!-- Input nativo: @ref es ElementReference; capturamos Enter -->
            <input class="form-control"
                id="scan"
                @bind="InputCode"
                @bind:event="oninput"
                @onkeydown="HandleKeyDown"
                autocomplete="off"
                spellcheck="false"
                @ref="scanInput" />
        </div>

        <button type="button" class="btn btn-secondary mt-2" @onclick="HandleScan">Buscar</button>
    </div>

    @if (HasLastScan)
    {
        var last = StockList.First();
        <div class="alert alert-info small">
            Último escaneado: <strong>@last.ProductName</strong> | Lote <strong>@last.Batch</strong> | Venc: <strong>@last.Expiration?.ToShortDateString()</strong>
        </div>
    }
    <div class="stockselector-table-wrap" style="zoom:80%">
        <table class="table table-sm table-bordered stockselector-table">
            <thead class="table-light">
                <tr>
                    <th class="text-left align-middle">Producto</th>
                    <th class="text-center align-middle">Lote</th>
                    <th class="text-center align-middle">Serial</th>
                    <th class="text-center align-middle">Vencimiento</th>
                    <th class="text-center align-middle">Disponible</th>
                    <th class="text-center align-middle">Cantidad a usar</th>
                </tr>
            </thead>
            <tbody>
                @foreach (var item in StockList)
                {
                    <tr>
                        <td>@item.ProductName</td>
                        <td class="text-center align-middle">@item.Batch</td>
                        <td class="text-center align-middle">@item.Serial</td>
                        <td class="text-center align-middle">@item.Expiration?.ToShortDateString()</td>
                        <td class="text-center align-middle">@item.Available.ToString("N2", CultureInfo.CurrentCulture)</td>
                        <td>
                            <input type="number"
                                   value="@(item.Selected.ToString("G29"))"
                                   @onchange="@(n => item.Selected = decimal.Parse(n.Value.ToString(), NumberStyles.Any, CultureInfo.InvariantCulture))"
                                   min="0"
                                   max="@(item.Available.ToString("G29"))"
                                   class="form-control form-control-sm" />
                        </td>
                    </tr>
                }
            </tbody>
        </table>

    </div>
</div>

<div class="modal-footer">
    <button class="btn btn-secondary" @onclick="Cancel">Cancelar</button>
    <button class="btn btn-primary" @onclick="Confirm">Agregar a lista</button>
</div>

@code {
    [CascadingParameter] BlazoredModalInstance ModalInstance { get; set; } = default!;

    [Parameter] public int? ProductId { get; set; }
    [Parameter] public int? LocationId { get; set; }
    [Parameter] public List<ProductSetItemDto>? SetItems { get; set; }
    [Parameter] public List<StockSnapshotItem> Snapshot { get; set; } = new();

    // Mapa: BusinessKey -> Cantidad ya en la expedición
    private Dictionary<string, decimal> _alreadyInExpedition = new();
    private bool HasLastScan { get; set; }

    //private readonly List<StockItemSelectionDto> SelectedItems = new();
    private List<StockDisplayRow> StockList = new();
    private string InputCode { get; set; } = string.Empty;
    private ElementReference scanInput;

    protected override void OnParametersSet()
    {
        HasLastScan = false;
        _alreadyInExpedition = Snapshot
            .GroupBy(x => x.BusinessKey)
            .ToDictionary(g => g.Key, g => g.Sum(x => x.Quantity));

        // Precargar la tabla del modal con lo que ya estaba en la expedición
        StockList = Snapshot.Select(s => new StockDisplayRow
        {
            ProductId = s.ProductId,
            ProductName = s.ProductName ?? "<Producto sin descripción>",
            Batch = s.Batch,
            Serial = s.Serial,
            Expiration = s.Expiration?.ToDateTime(TimeOnly.MinValue),
            LocationId = s.LocationId,
            TraceabilityType = s.TraceabilityType,
            BusinessKey = s.BusinessKey,
            Selected = s.Quantity,     // cantidad actual en la expedición
            Available = 0              // opcional: si querés recalcular, traer de BD
        }).ToList();
    }

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
            await scanInput.FocusAsync();
    }

    private async Task OnKeyDown(KeyboardEventArgs e)
    {
        if (e.Key == "Enter")
            await HandleScan();
    }

    private async Task HandleKeyDown(KeyboardEventArgs e)
    {
        if (e.Key is "Enter" or "NumpadEnter")
        {
            await HandleScan();
            await scanInput.FocusAsync();
        }
    }

    private async Task HandleScan()
    {
        try
        {
            var raw = InputCode?.Trim();
            if (string.IsNullOrWhiteSpace(raw))
                return;

            // Parsear y resolver candidato desde backend
            var s = await stockScanService.ParseAndMatchAsync(raw, LocationId ?? 1);

            // limpiar input y devolver foco
            InputCode = string.Empty;
            //wait Refocus();

            if (s is null)
            {
                toastService.ShowWarning("No se encontró un artículo para ese código.");
                return;
            }
            HasLastScan = true;

            // Mapear a fila del modal
            var row = new StockDisplayRow
            {
                StockItemId = s.StockItemId,
                ProductId = s.ProductId,
                ProductName = s.ProductName ?? string.Empty,
                Batch = s.Batch ?? string.Empty,
                Serial = s.Serial ?? string.Empty,
                Expiration = s.Expiration,
                LocationId = s.LocationId,
                Available = s.Quantity,
                TraceabilityType = s.TraceabilityType,
                Selected = 0
            };

            // Clave de negocio + contexto del snapshot
            row.BusinessKey = BuildKeyFromRow(row);

            var alreadyInExpedition = _alreadyInExpedition.TryGetValue(row.BusinessKey, out var q)
                ? q
                : 0m;

            var selectedInModal = StockList
                .Where(x => x.BusinessKey == row.BusinessKey)
                .Sum(x => x.Selected);

            var effectiveAvailable = row.Available  - selectedInModal;

            // Reglas por trazabilidad
            // SERIAL*: nunca duplicar ni incrementar
            if (!string.IsNullOrWhiteSpace(row.Serial))
            {
                // buscar si ya existe la misma BusinessKey en la tabla del modal
                var existingSerial = StockList.FirstOrDefault(x => x.BusinessKey == row.BusinessKey);

                // 🔄 REFRESH siempre que tengamos la fila (si existe), antes de cualquier return
                if (existingSerial is not null)
                {
                    existingSerial.Available = row.Available ;   // actualizar disponible con el del re-escaneo
                }

                var existsInModal = existingSerial is not null;

                //var existsInModal = StockList.Any(x => x.BusinessKey == row.BusinessKey);
                if (alreadyInExpedition > 0 || existsInModal)
                {
                    toastService.ShowWarning("Este serial ya está agregado.");
                    await Refocus();
                    return;
                }

                if (effectiveAvailable <= 0)
                {
                    toastService.ShowWarning("No hay disponible para este serial.");
                    await Refocus();
                    return;
                }

                row.Selected = 1;                 // serial siempre = 1
                StockList.Insert(0, row);
                await Refocus();
                return;
            }

            // BATCH / NONE: sumar sin pasar el disponible efectivo
            if (effectiveAvailable <= 0)
            {
                var existingSerial = StockList.FirstOrDefault(x => x.BusinessKey == row.BusinessKey);

                // 🔄 REFRESH siempre que tengamos la fila (si existe), antes de cualquier return
                if (existingSerial is not null)
                {
                    existingSerial.Available = row.Available;   // actualizar disponible con el del re-escaneo
                }
                toastService.ShowWarning("No hay más disponible para esta combinación.");
                await Refocus();
                return;
            }

            var existingBN = StockList.FirstOrDefault(x => x.BusinessKey == row.BusinessKey);
            if (existingBN is not null)
            {
                existingBN.Available = s.Quantity;
                // suma +1 cap al disponible efectivo
                var add = 1m;
                var maxAdd = Math.Min(add, effectiveAvailable);
                if (maxAdd <= 0)
                {
                    toastService.ShowWarning("Se alcanzó el máximo disponible.");
                    await Refocus();
                    return;
                }

                existingBN.Selected += maxAdd;
                await Refocus();
                return;
            }

            // Nueva fila (otra ubicación/lote/etc.)
            row.Selected = 1;
            StockList.Insert(0, row);
            await Refocus();
        }
        catch (Exception ex)
        {
            toastService.ShowError($"Error al procesar el escaneo: {ex.Message}");
            await Refocus();
        }
    }

    private async Task Refocus()
    {
        await Task.Yield();           // asegura que el DOM está listo
        await scanInput.FocusAsync(); // deja el cursor listo para el próximo escaneo
    }

    private async Task Confirm()
    {
        var selected = StockList
            .Where(x => x.Selected > 0)
            .Select(x => new StockItemSelectionDto
            {
                StockItemId = x.StockItemId,
                ProductId = x.ProductId,
                ProductName = x.ProductName,
                Batch = x.Batch,
                Serial= x.Serial,
                Expiration = x.Expiration,
                TraceabilityType = x.TraceabilityType,
                LocationId = x.LocationId,
                Quantity = !string.IsNullOrWhiteSpace(x.Serial)
                    ? 1m
                    : decimal.Round(Math.Min(x.Selected, x.Available), 2, MidpointRounding.AwayFromZero)

            })
            .ToList();

        if (!selected.Any())
        {
            toastService.ShowWarning("No se seleccionó ningún producto.");
            return;
        }
        await ModalInstance.CloseAsync(ModalResult.Ok(selected));
    }

    private Task Cancel() => ModalInstance.CancelAsync();

    public class StockDisplayRow
    {
        public int StockItemId { get; set; }
        public int ProductId { get; set; }
        public string ProductName { get; set; } = string.Empty;
        public string Batch { get; set; } = string.Empty;
        public string Serial { get; set; } = string.Empty;
        public DateTime? Expiration { get; set; }
        public decimal Available { get; set; }
        public decimal Selected { get; set; }
        public int LocationId { get; set; }
        public int TraceabilityType { get; set; }

        /// <summary>Clave de fusión (no se persiste).</summary>
        public string BusinessKey { get; set; } = string.Empty;
    }

    private static string BuildKeyFromRow(StockDisplayRow r)
    {
        return Domain.Dtos.Stock.StockKeys.BuildBusinessKey(
            r.ProductId,
            r.LocationId,
            r.Batch,
            r.Expiration.HasValue ? DateOnly.FromDateTime(r.Expiration.Value) : (DateOnly?)null,
            r.Serial
        );
    }
}
