using System.Net.Http.Json; using Domain.Dtos.Stock; using Domain.Generics; //using Transversal.Services; public class StockScanService : IStockScanService { private readonly HttpClient _http; public StockScanService(HttpClient http) { _http = http; } public async Task ParseAndMatchAsync(string rawInput, int locationId) { if (string.IsNullOrWhiteSpace(rawInput)) return null; var payload = new { Raw = rawInput, LocationId = locationId, Page = 1, PageSize = 10 }; var resp = await _http.PostAsJsonAsync("/api/lsstockscan/parse-and-search", payload); if (!resp.IsSuccessStatusCode) return null; var pr = await resp.Content.ReadFromJsonAsync>(); var first = pr?.Items?.FirstOrDefault(); if (first is null) return null; return new StockItemSelectionDto { StockItemId = first.StockItemId, ProductId = first.ProductId, ProductName = first.ProductName, Batch = first.Batch ?? string.Empty, Expiration = first.Expiration?.ToDateTime(TimeOnly.MinValue), Quantity = first.AvailableQty, LocationId = first.LocationId ?? locationId, Serial = first.Serial // si lo devolvés en el DTO de scan }; } //public async Task ParseAndMatchAsync(string rawInput, int locationId) //{ // if (string.IsNullOrWhiteSpace(rawInput)) // return null; // try // { // var parsed = new Gs1ScanResult(); // //var parsed = ""; // var raw = rawInput.Trim(); // bool hasParsedAis = !string.IsNullOrWhiteSpace(parsed.Lot) // || parsed.ExpirationDate.HasValue // || !string.IsNullOrWhiteSpace(parsed.Serial) // || !string.IsNullOrWhiteSpace(parsed.Variant); // incluir (22) // string? gtinToSend = parsed.Gtin ?? parsed.Variant; // (22) como fallback // if (gtinToSend is null && !hasParsedAis && IsPlainCode(raw)) // gtinToSend = raw; // código plano tipeado (factory/regulatory) // // 3. Armar parámetros de búsqueda // var sp = new StockItemParsedSearchParams // { // Gtin = gtinToSend, // Batch = string.IsNullOrWhiteSpace(parsed.Lot) ? null : parsed.Lot, // Expiration = parsed.ExpirationDate.HasValue // ? DateOnly.FromDateTime(parsed.ExpirationDate.Value) // : null, // Serial = string.IsNullOrWhiteSpace(parsed.Serial) ? null : parsed.Serial, // LocationId = locationId, // Page = 1, // PageSize = 10 // }; // // 4. Log para depuración (quitar en producción) // Console.WriteLine($"[ParseAndMatchAsync] Gtin={sp.Gtin}, Batch={sp.Batch}, Exp={sp.Expiration}, Serial={sp.Serial}, Loc={sp.LocationId}"); // // 5. Llamar a la API // var resp = await _http.PostAsJsonAsync("/api/lsstockscan/search-parsed", sp); // if (!resp.IsSuccessStatusCode) // { // var err = await resp.Content.ReadAsStringAsync(); // Console.WriteLine($"[ParseAndMatchAsync] API devolvió error {resp.StatusCode}: {err}"); // return null; // } // // 6. Leer resultado // var pr = await resp.Content.ReadFromJsonAsync>(); // var first = pr?.Items?.FirstOrDefault(); // if (first == null) // { // Console.WriteLine("[ParseAndMatchAsync] No se encontró ningún ítem que coincida."); // return null; // } // // 7. Mapear a DTO de selección // return new StockItemSelectionDto // { // StockItemId = first.StockItemId, // ProductId = first.ProductId, // ProductName = first.ProductName, // Batch = first.Batch ?? string.Empty, // Expiration = first.Expiration?.ToDateTime(TimeOnly.MinValue), // Quantity = first.AvailableQty, // LocationId = first.LocationId ?? 0 // }; // } // catch (Exception ex) // { // Console.WriteLine($"[ParseAndMatchAsync] Error inesperado: {ex}"); // throw; // } //} //private static bool IsPlainCode(string s) //{ // // sin FNC1 ($), sin espacios y sin prefijos AI típicos // if (s.Contains('$') || s.Contains((char)29) || s.Contains(' ')) return false; // // evita raws que empiezan como AIs "01","10","17","21","22" // var prefix = s.Length >= 2 ? s[..2] : s; // if (prefix is "01" or "10" or "11" or "17" or "21" or "22") return false; // // permite letras/dígitos y algunos separadores comunes // return s.All(c => char.IsLetterOrDigit(c) || c is '-' or '/' or '_'); //} }