using GestionIntegral.Api.Data;
using GestionIntegral.Api.Data.Repositories.Distribucion;
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 CambioParadaService : ICambioParadaService
  {
    private readonly ICambioParadaRepository _paradaRepo;
    private readonly ICanillaRepository _canillaRepo; // Para nombre y validación
    private readonly DbConnectionFactory _connectionFactory;
    private readonly ILogger<CambioParadaService> _logger;

    public CambioParadaService(
        ICambioParadaRepository paradaRepo,
        ICanillaRepository canillaRepo,
        DbConnectionFactory connectionFactory,
        ILogger<CambioParadaService> logger)
    {
      _paradaRepo = paradaRepo;
      _canillaRepo = canillaRepo;
      _connectionFactory = connectionFactory;
      _logger = logger;
    }

    private async Task<CambioParadaDto> MapToDto(CambioParadaCanilla parada)
    {
      var canillaData = await _canillaRepo.GetByIdAsync(parada.IdCanilla);
      return new CambioParadaDto
      {
        IdRegistro = parada.IdRegistro,
        IdCanilla = parada.IdCanilla,
        NombreCanilla = canillaData.Canilla?.NomApe ?? "N/A",
        Parada = parada.Parada,
        VigenciaD = parada.VigenciaD.ToString("yyyy-MM-dd"),
        VigenciaH = parada.VigenciaH?.ToString("yyyy-MM-dd")
      };
    }

    public async Task<IEnumerable<CambioParadaDto>> ObtenerPorCanillaAsync(int idCanilla)
    {
      var paradas = await _paradaRepo.GetByCanillaAsync(idCanilla);
      var dtos = new List<CambioParadaDto>();
      foreach (var p in paradas) { dtos.Add(await MapToDto(p)); }
      return dtos;
    }

    public async Task<CambioParadaDto?> ObtenerPorIdAsync(int idRegistro)
    {
      var parada = await _paradaRepo.GetByIdAsync(idRegistro);
      return parada == null ? null : await MapToDto(parada);
    }

    public async Task<(CambioParadaDto? Parada, string? Error)> CrearNuevaParadaAsync(int idCanilla, CreateCambioParadaDto createDto, int idUsuario)
    {
      var canilla = await _canillaRepo.GetByIdSimpleAsync(idCanilla);
      if (canilla == null) return (null, "Canillita no encontrado.");

      if (createDto.VigenciaD.Date < DateTime.Today.AddYears(-5) || createDto.VigenciaD.Date > DateTime.Today.AddYears(5)) // Validación básica de fecha
        return (null, "Fecha de Vigencia Desde inválida.");


      using var connection = _connectionFactory.CreateConnection();
      if (connection.State != ConnectionState.Open) { if (connection is System.Data.Common.DbConnection dbConn) await dbConn.OpenAsync(); else connection.Open(); }
      using var transaction = connection.BeginTransaction();
      try
      {
        // 1. Buscar la parada activa actual (si existe) para cerrarla
        var paradaActual = await _paradaRepo.GetCurrentParadaAsync(idCanilla, transaction);
        if (paradaActual != null)
        {
          if (createDto.VigenciaD.Date <= paradaActual.VigenciaD.Date)
          {
            transaction.Rollback();
            return (null, "La Vigencia Desde de la nueva parada debe ser posterior a la Vigencia Desde de la parada activa actual.");
          }
          // Cerrar la parada actual estableciendo VigenciaH al día anterior de la nueva VigenciaD
          DateTime vigenciaHAactual = createDto.VigenciaD.Date.AddDays(-1);
          bool cerrada = await _paradaRepo.UpdateVigenciaHAsync(paradaActual.IdRegistro, vigenciaHAactual, idUsuario, transaction);
          if (!cerrada) throw new DataException("No se pudo cerrar la parada activa anterior.");
          _logger.LogInformation("Parada anterior ID {IdRegistro} cerrada con VigenciaH {VigenciaH}", paradaActual.IdRegistro, vigenciaHAactual);
        }
        // 2. Crear la nueva parada
        var nuevaParada = new CambioParadaCanilla
        {
          IdCanilla = idCanilla,
          Parada = createDto.Parada,
          VigenciaD = createDto.VigenciaD.Date,
          VigenciaH = null // Nueva parada siempre inicia activa
        };
        var paradaCreada = await _paradaRepo.CreateAsync(nuevaParada, idUsuario, transaction);
        if (paradaCreada == null) throw new DataException("Error al crear el nuevo registro de parada.");

        // 3. Actualizar la parada principal en dist_dtCanillas
        canilla.Parada = paradaCreada.Parada; // Actualizar el campo Parada en la tabla principal
        bool canillaActualizado = await _canillaRepo.UpdateAsync(canilla, idUsuario, transaction); // Asume que tu repo de canilla tiene UpdateAsync que toma la entidad
        if (!canillaActualizado) throw new DataException("Error al actualizar la parada principal del canillita.");


        transaction.Commit();
        _logger.LogInformation("Nueva parada ID {IdRegistro} creada para Canilla ID {IdCanilla}. Parada principal actualizada.", paradaCreada.IdRegistro, idCanilla);
        return (await MapToDto(paradaCreada), null);
      }
      catch (Exception ex)
      {
        try { transaction.Rollback(); } catch { }
        _logger.LogError(ex, "Error en CrearNuevaParadaAsync para Canilla ID {IdCanilla}", idCanilla);
        return (null, $"Error interno: {ex.Message}");
      }
      finally { if (connection.State == ConnectionState.Open) { if (connection is System.Data.Common.DbConnection dbConn) await dbConn.CloseAsync(); else connection.Close(); } }
    }

    public async Task<(bool Exito, string? Error)> CerrarParadaAsync(int idRegistro, UpdateCambioParadaDto updateDto, int idUsuario)
    {
      // Este método es para cerrar una parada manualmente si es necesario,
      // por ejemplo, si se cometió un error y no se creó una nueva que la cierre.
      var paradaExistente = await _paradaRepo.GetByIdAsync(idRegistro);
      if (paradaExistente == null) return (false, "Registro de parada no encontrado.");
      if (paradaExistente.VigenciaH.HasValue) return (false, "Esta parada ya tiene una fecha de Vigencia Hasta.");
      if (updateDto.VigenciaH.Date < paradaExistente.VigenciaD.Date)
        return (false, "La Vigencia Hasta no puede ser anterior a la Vigencia Desde.");

      using var connection = _connectionFactory.CreateConnection();
      if (connection.State != ConnectionState.Open) { if (connection is System.Data.Common.DbConnection dbConn) await dbConn.OpenAsync(); else connection.Open(); }
      using var transaction = connection.BeginTransaction();
      try
      {
        bool actualizada = await _paradaRepo.UpdateVigenciaHAsync(idRegistro, updateDto.VigenciaH.Date, idUsuario, transaction);
        if (!actualizada) throw new DataException("Error al actualizar la Vigencia Hasta de la parada.");

        // Opcional: Si esta era la parada activa en dist_dtCanillas, podrías querer limpiarla o marcarla de alguna forma
        // Esto depende de si el campo 'Parada' en dist_dtCanillas siempre debe reflejar la última activa.
        // Por simplicidad, no se actualiza dist_dtCanillas aquí al cerrar una parada manualmente.

        transaction.Commit();
        _logger.LogInformation("VigenciaH actualizada para Parada ID {IdRegistro}", idRegistro);
        return (true, null);
      }
      catch (Exception ex)
      {
        try { transaction.Rollback(); } catch { }
        _logger.LogError(ex, "Error al cerrar Parada ID {IdRegistro}", idRegistro);
        return (false, $"Error interno: {ex.Message}");
      }
      finally { if (connection.State == ConnectionState.Open) { if (connection is System.Data.Common.DbConnection dbConn) await dbConn.CloseAsync(); else connection.Close(); } }
    }

    public async Task<(bool Exito, string? Error)> EliminarParadaAsync(int idRegistro, int idUsuario)
    {
      // La eliminación puede ser problemática si rompe la secuencia histórica.
      // Considera si realmente quieres permitir esto o solo "cerrar" paradas.
      var paradaExistente = await _paradaRepo.GetByIdAsync(idRegistro);
      if (paradaExistente == null) return (false, "Registro de parada no encontrado.");
      if (paradaExistente.VigenciaH == null)
        return (false, "No se puede eliminar una parada activa. Primero debe cerrarla o crear una nueva que la reemplace.");

      using var connection = _connectionFactory.CreateConnection();
      if (connection.State != ConnectionState.Open) { if (connection is System.Data.Common.DbConnection dbConn) await dbConn.OpenAsync(); else connection.Open(); }
      using var transaction = connection.BeginTransaction();
      try
      {
        bool eliminado = await _paradaRepo.DeleteAsync(idRegistro, idUsuario, transaction);
        if (!eliminado) throw new DataException("Error al eliminar el registro de parada.");

        transaction.Commit();
        _logger.LogInformation("Registro de Parada ID {IdRegistro} eliminado por Usuario ID {IdUsuario}", idRegistro, idUsuario);
        return (true, null);
      }
      catch (Exception ex)
      {
        try { transaction.Rollback(); } catch { }
        _logger.LogError(ex, "Error al eliminar Parada ID {IdRegistro}", idRegistro);
        return (false, $"Error interno: {ex.Message}");
      }
      finally { if (connection.State == ConnectionState.Open) { if (connection is System.Data.Common.DbConnection dbConn) await dbConn.CloseAsync(); else connection.Close(); } }
    }

    public async Task<IEnumerable<CambioParadaHistorialDto>> ObtenerCambiosParadaHistorialAsync(
        DateTime? fechaDesde, DateTime? fechaHasta,
        int? idUsuarioModifico, string? tipoModificacion,
        int? idCanillaAfectado)
        {
            var historialData = await _paradaRepo.GetCambiosParadaHistorialAsync(fechaDesde, fechaHasta, idUsuarioModifico, tipoModificacion, idCanillaAfectado);

            return historialData.Select(h => new CambioParadaHistorialDto
            {
                Id_Registro = h.Historial.Id_Registro,
                Id_Canilla = h.Historial.Id_Canilla,
                NombreCanilla = h.NombreCanilla,
                Parada = h.Historial.Parada,
                VigenciaD = h.Historial.VigenciaD,
                VigenciaH = h.Historial.VigenciaH ?? default(DateTime),
                Id_Usuario = h.Historial.Id_Usuario,
                NombreUsuarioModifico = h.NombreUsuarioModifico,
                FechaMod = h.Historial.FechaMod,
                TipoMod = h.Historial.TipoMod
            }).ToList();
        }
  }
}