using Domain.Dtos.Stock;
using Domain.Entities;
using Domain.Generics;
using Microsoft.EntityFrameworkCore;
using Models.Helpers;
using Models.Interfaces;
using Models.Models;

namespace Models.Repositories
{
    public class PhLSMProductRepository(PhronCareOperationsHubContext context) : IPhLSMProductRepository
    {
        private readonly PhronCareOperationsHubContext _context = context;

        public async Task<PagedResult<ELSProduct>> SearchAsync(LSProductSearchParams searchParams)
        {
            var query = _context.PhLsmProducts
                .Include(p => p.Division)
                .Include(p => p.Unit)
                .AsQueryable();

            if (!string.IsNullOrWhiteSpace(searchParams.Code))
            {
                var lowered = searchParams.Code.ToLower();
                query = query.Where(p =>
                    (!string.IsNullOrEmpty(p.FactoryCode) && p.FactoryCode.ToLower().Contains(lowered)) ||
                    (!string.IsNullOrEmpty(p.ExternalCode) && p.ExternalCode.ToLower().Contains(lowered)));
            }

            if (!string.IsNullOrWhiteSpace(searchParams.Description))
            {
                var lowered = searchParams.Description.ToLower();
                query = query.Where(p =>
                    (!string.IsNullOrEmpty(p.Name) && p.Name.ToLower().Contains(lowered)) ||
                    (!string.IsNullOrEmpty(p.Descripcion) && p.Descripcion.ToLower().Contains(lowered)));
            }

            if (searchParams.ProductType.HasValue)
                query = query.Where(p => p.ProductType == searchParams.ProductType);

            if (searchParams.TraceabilityType.HasValue)
                query = query.Where(p => p.TraceabilityType == searchParams.TraceabilityType);

            if (searchParams.DivisionId.HasValue)
                query = query.Where(p => p.DivisionId == searchParams.DivisionId);

            if (searchParams.UnitId.HasValue)
                query = query.Where(p => p.UnitId == searchParams.UnitId);

            if (searchParams.PlusProcess.HasValue)
                query = query.Where(p => p.PlusProcess == searchParams.PlusProcess);

            var paged = await query.ToPagedResultAsync(searchParams.Page, searchParams.PageSize);

            return new PagedResult<ELSProduct>
            {
                Items = paged.Items.Select(EntityMapper.MapEntity<PhLsmProduct, ELSProduct>),
                TotalItems = paged.TotalItems,
                Page = paged.Page,
                PageSize = paged.PageSize
            };
        }

        public async Task<ELSProduct?> GetByIdAsync(int id)
        {
            var entity = await _context.PhLsmProducts
                .Include(p => p.Division)
                .Include(p => p.Unit)
                .FirstOrDefaultAsync(p => p.Id == id);

            return entity != null ? EntityMapper.MapEntity<PhLsmProduct, ELSProduct>(entity) : null;
        }

        public async Task<ELSProduct> CreateAsync(ELSProduct entity)
        {
            var mapped = EntityMapper.MapEntity<ELSProduct, PhLsmProduct>(entity);
            _context.PhLsmProducts.Add(mapped);
            await _context.SaveChangesAsync();
            return EntityMapper.MapEntity<PhLsmProduct, ELSProduct>(mapped);
        }

        public async Task<bool> UpdateAsync(ELSProduct entity)
        {
            var existing = await _context.PhLsmProducts.FindAsync(entity.Id);
            if (existing == null) return false;

            EntityMapper.MapEntityToExisting(entity, existing);
            await _context.SaveChangesAsync();
            return true;
        }

        public async Task<bool> DeleteAsync(int id)
        {
            var entity = await _context.PhLsmProducts.FindAsync(id);
            if (entity == null) return false;

            _context.PhLsmProducts.Remove(entity);
            await _context.SaveChangesAsync();
            return true;
        }

        public async Task<ProductImportResultDto> ImportProductsAsync(List<ProductImportPreviewDto> items)
        {
            // 1. Prevenir nulos/vacíos
            if (items == null || items.Count == 0)
                return new ProductImportResultDto { Inserted = 0, Skipped = 0 };

            // 2. Obtener todos los códigos únicos de División y Unidad
            var divisionCodes = items.Select(x => x.DivisionCode).Distinct().ToList();
            var unitCodes = items.Select(x => x.UnitCode).Distinct().ToList();

            // 3. Mapear a IDs desde la base
            var divisionMap = await _context.PhLsmProductDivisions
                .Where(d => divisionCodes.Contains(d.Code))
                .ToDictionaryAsync(d => d.Code, d => d.Id);

            var unitMap = await _context.PhLsmUnitOfMeasures
                .Where(u => unitCodes.Contains(u.Code))
                .ToDictionaryAsync(u => u.Code, u => u.Id);

            // 4. Armar entidades para insertar (sólo si las FK existen)
            var toInsert = new List<PhLsmProduct>();
            int skipped = 0;

            foreach (var item in items)
            {
                // Validaciones de existencia de Division y Unidad
                if (!divisionMap.TryGetValue(item.DivisionCode, out var divisionId)
                    || !unitMap.TryGetValue(item.UnitCode, out var unitId))
                {
                    skipped++;
                    continue; // Saltea el producto si alguna FK no existe
                }

                // Armá la entidad
                var entity = new PhLsmProduct
                {
                    FactoryCode = item.FactoryCode,
                    Name = item.Name,
                    Descripcion = item.Description,
                    ProductType = item.ProductType,
                    TraceabilityType = item.TraceabilityType,
                    DivisionId = divisionId,
                    UnitId = unitId,
                    PlusProcess = item.PlusProcess,
                    ExternalCode = item.ExternalCode,
                    RegulatoryCode = item.RegulatoryCode
                    // otros campos...
                };

                toInsert.Add(entity);
            }

            // 5. Insertar en batch
            if (toInsert.Count > 0)
            {
                _context.PhLsmProducts.AddRange(toInsert);
                await _context.SaveChangesAsync();
            }

            // 6. Retornar resumen
            return new ProductImportResultDto
            {
                Inserted = toInsert.Count,
                Skipped = skipped
            };
        }

    }
}
