using GestionIntegral.Api.Data;
using GestionIntegral.Api.Data.Repositories.Distribucion;
using GestionIntegral.Api.Dtos.Auditoria;
using GestionIntegral.Api.Dtos.Distribucion;
using GestionIntegral.Api.Models.Distribucion;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Threading.Tasks;

namespace GestionIntegral.Api.Services.Distribucion
{
    public class ControlDevolucionesService : IControlDevolucionesService
    {
        private readonly IControlDevolucionesRepository _controlDevRepo;
        private readonly IEmpresaRepository _empresaRepository; // Para validar IdEmpresa y obtener nombre
        private readonly DbConnectionFactory _connectionFactory;
        private readonly ILogger<ControlDevolucionesService> _logger;

        public ControlDevolucionesService(
            IControlDevolucionesRepository controlDevRepo,
            IEmpresaRepository empresaRepository,
            DbConnectionFactory connectionFactory,
            ILogger<ControlDevolucionesService> logger)
        {
            _controlDevRepo = controlDevRepo;
            _empresaRepository = empresaRepository;
            _connectionFactory = connectionFactory;
            _logger = logger;
        }

        private async Task<ControlDevolucionesDto> MapToDto(ControlDevoluciones control)
        {
            if (control == null) return null!;
            var empresa = await _empresaRepository.GetByIdAsync(control.IdEmpresa);
            return new ControlDevolucionesDto
            {
                IdControl = control.IdControl,
                IdEmpresa = control.IdEmpresa,
                NombreEmpresa = empresa?.Nombre ?? "Empresa Desconocida",
                Fecha = control.Fecha.ToString("yyyy-MM-dd"),
                Entrada = control.Entrada,
                Sobrantes = control.Sobrantes,
                Detalle = control.Detalle,
                SinCargo = control.SinCargo
            };
        }

        public async Task<IEnumerable<ControlDevolucionesDto>> ObtenerTodosAsync(DateTime? fechaDesde, DateTime? fechaHasta, int? idEmpresa)
        {
            var controles = await _controlDevRepo.GetAllAsync(fechaDesde, fechaHasta, idEmpresa);
            var dtos = new List<ControlDevolucionesDto>();
            foreach (var control in controles)
            {
                dtos.Add(await MapToDto(control));
            }
            return dtos;
        }

        public async Task<ControlDevolucionesDto?> ObtenerPorIdAsync(int idControl)
        {
            var control = await _controlDevRepo.GetByIdAsync(idControl);
            return control == null ? null : await MapToDto(control);
        }

        public async Task<(ControlDevolucionesDto? Control, string? Error)> CrearAsync(CreateControlDevolucionesDto createDto, int idUsuario)
        {
            var empresa = await _empresaRepository.GetByIdAsync(createDto.IdEmpresa);
            if (empresa == null)
                return (null, "La empresa especificada no existe.");

            // Validar que no exista ya un control para esa empresa y fecha
            if (await _controlDevRepo.GetByEmpresaAndFechaAsync(createDto.IdEmpresa, createDto.Fecha.Date) != null)
            {
                return (null, $"Ya existe un control de devoluciones para la empresa '{empresa.Nombre}' en la fecha {createDto.Fecha:dd/MM/yyyy}.");
            }

            var nuevoControl = new ControlDevoluciones
            {
                IdEmpresa = createDto.IdEmpresa,
                Fecha = createDto.Fecha.Date,
                Entrada = createDto.Entrada,
                Sobrantes = createDto.Sobrantes,
                Detalle = createDto.Detalle,
                SinCargo = createDto.SinCargo
            };

            using var connection = _connectionFactory.CreateConnection();
            if (connection is System.Data.Common.DbConnection dbConn) await dbConn.OpenAsync(); else connection.Open();
            using var transaction = connection.BeginTransaction();
            try
            {
                var controlCreado = await _controlDevRepo.CreateAsync(nuevoControl, idUsuario, transaction);
                if (controlCreado == null) throw new DataException("Error al registrar el control de devoluciones.");

                transaction.Commit();
                _logger.LogInformation("ControlDevoluciones ID {Id} creado por Usuario ID {UserId}.", controlCreado.IdControl, idUsuario);
                return (await MapToDto(controlCreado), null);
            }
            catch (Exception ex)
            {
                try { transaction.Rollback(); } catch { }
                _logger.LogError(ex, "Error CrearAsync ControlDevoluciones para Empresa ID {IdEmpresa}, Fecha {Fecha}", createDto.IdEmpresa, createDto.Fecha);
                return (null, $"Error interno: {ex.Message}");
            }
        }

        public async Task<(bool Exito, string? Error)> ActualizarAsync(int idControl, UpdateControlDevolucionesDto updateDto, int idUsuario)
        {
            using var connection = _connectionFactory.CreateConnection();
            if (connection is System.Data.Common.DbConnection dbConn) await dbConn.OpenAsync(); else connection.Open();
            using var transaction = connection.BeginTransaction();
            try
            {
                var controlExistente = await _controlDevRepo.GetByIdAsync(idControl); // Obtener dentro de TX
                if (controlExistente == null) return (false, "Control de devoluciones no encontrado.");

                // IdEmpresa y Fecha no se modifican
                controlExistente.Entrada = updateDto.Entrada;
                controlExistente.Sobrantes = updateDto.Sobrantes;
                controlExistente.Detalle = updateDto.Detalle;
                controlExistente.SinCargo = updateDto.SinCargo;

                var actualizado = await _controlDevRepo.UpdateAsync(controlExistente, idUsuario, transaction);
                if (!actualizado) throw new DataException("Error al actualizar el control de devoluciones.");

                transaction.Commit();
                _logger.LogInformation("ControlDevoluciones ID {Id} actualizado por Usuario ID {UserId}.", idControl, idUsuario);
                return (true, null);
            }
            catch (KeyNotFoundException) { try { transaction.Rollback(); } catch { } return (false, "Registro no encontrado."); }
            catch (Exception ex)
            {
                try { transaction.Rollback(); } catch { }
                _logger.LogError(ex, "Error ActualizarAsync ControlDevoluciones ID: {Id}", idControl);
                return (false, $"Error interno: {ex.Message}");
            }
        }

        public async Task<(bool Exito, string? Error)> EliminarAsync(int idControl, int idUsuario)
        {
            using var connection = _connectionFactory.CreateConnection();
            if (connection is System.Data.Common.DbConnection dbConn) await dbConn.OpenAsync(); else connection.Open();
            using var transaction = connection.BeginTransaction();
            try
            {
                var controlExistente = await _controlDevRepo.GetByIdAsync(idControl); // Obtener dentro de TX
                if (controlExistente == null) return (false, "Control de devoluciones no encontrado.");

                // Aquí no hay dependencias directas que afecten saldos, es un registro informativo.
                // Se podría verificar si está "en uso" si alguna lógica de reporte o cierre depende de él,
                // pero por ahora, la eliminación es directa.

                var eliminado = await _controlDevRepo.DeleteAsync(idControl, idUsuario, transaction);
                if (!eliminado) throw new DataException("Error al eliminar el control de devoluciones.");

                transaction.Commit();
                _logger.LogInformation("ControlDevoluciones ID {Id} eliminado por Usuario ID {UserId}.", idControl, idUsuario);
                return (true, null);
            }
            catch (KeyNotFoundException) { try { transaction.Rollback(); } catch { } return (false, "Registro no encontrado."); }
            catch (Exception ex)
            {
                try { transaction.Rollback(); } catch { }
                _logger.LogError(ex, "Error EliminarAsync ControlDevoluciones ID: {Id}", idControl);
                return (false, $"Error interno: {ex.Message}");
            }
        }

        public async Task<IEnumerable<ControlDevolucionesHistorialDto>> ObtenerHistorialAsync(
        DateTime? fechaDesde, DateTime? fechaHasta,
        int? idUsuarioModifico, string? tipoModificacion,
        int? idControlAfectado, int? idEmpresaAfectada, DateTime? fechaAfectada)
    {
        var historialData = await _controlDevRepo.GetHistorialAsync(fechaDesde, fechaHasta, idUsuarioModifico, tipoModificacion, idControlAfectado, idEmpresaAfectada, fechaAfectada);
        
        return historialData.Select(h => new ControlDevolucionesHistorialDto
        {
            Id_Control = h.Historial.Id_Control,
            Id_Empresa = h.Historial.Id_Empresa,
            Fecha = h.Historial.Fecha,
            Entrada = h.Historial.Entrada,
            Sobrantes = h.Historial.Sobrantes,
            Detalle = h.Historial.Detalle,
            SinCargo = h.Historial.SinCargo,
            Id_Usuario = h.Historial.Id_Usuario,
            NombreUsuarioModifico = h.NombreUsuarioModifico,
            FechaMod = h.Historial.FechaMod,
            TipoMod = h.Historial.TipoMod
            // Mapear NombreEmpresa aquí si lo añades al DTO y lo obtienes
        }).ToList();
    }
    }
}