using Dapper;
using GestionIntegral.Api.Models.Distribucion;
using Microsoft.Extensions.Logging; // Para ILogger
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text; // Para StringBuilder
using System.Threading.Tasks;

namespace GestionIntegral.Api.Data.Repositories.Distribucion
{
  public class CambioParadaRepository : ICambioParadaRepository
  {
    private readonly DbConnectionFactory _cf;
    private readonly ILogger<CambioParadaRepository> _logger;

    public CambioParadaRepository(DbConnectionFactory cf, ILogger<CambioParadaRepository> logger)
    {
      _cf = cf;
      _logger = logger;
    }

    private async Task LogHistorialAsync(CambioParadaCanilla paradaOriginal, int idUsuario, string tipoMod, IDbConnection connection, IDbTransaction? transaction)
    {
      var historial = new CambioParadaCanillaHistorial
      {
        Id_Registro = paradaOriginal.IdRegistro,
        Id_Canilla = paradaOriginal.IdCanilla,
        Parada = paradaOriginal.Parada,
        VigenciaD = paradaOriginal.VigenciaD,
        VigenciaH = paradaOriginal.VigenciaH,
        Id_Usuario = idUsuario,
        FechaMod = DateTime.Now,
        TipoMod = tipoMod
      };
      const string sqlHistorial = @"
                 INSERT INTO dbo.dist_CambiosParadasCanillas_H 
                     (Id_Registro, Id_Canilla, Parada, VigenciaD, VigenciaH, Id_Usuario, FechaMod, TipoMod)
                 VALUES 
                     (@Id_Registro, @Id_Canilla, @Parada, @VigenciaD, @VigenciaH, @Id_Usuario, @FechaMod, @TipoMod);";
      await connection.ExecuteAsync(sqlHistorial, historial, transaction);
    }


    public async Task<IEnumerable<CambioParadaCanilla>> GetByCanillaAsync(int idCanilla)
    {
      using var connection = _cf.CreateConnection();
      const string sql = @"
                 SELECT Id_Registro AS IdRegistro, Id_Canilla AS IdCanilla, Parada, VigenciaD, VigenciaH 
                 FROM dbo.dist_CambiosParadasCanillas 
                 WHERE Id_Canilla = @IdCanilla 
                 ORDER BY VigenciaD DESC, Id_Registro DESC;";
      return await connection.QueryAsync<CambioParadaCanilla>(sql, new { IdCanilla = idCanilla });
    }

    public async Task<CambioParadaCanilla?> GetByIdAsync(int idRegistro)
    {
      using var connection = _cf.CreateConnection();
      const string sql = @"
                 SELECT Id_Registro AS IdRegistro, Id_Canilla AS IdCanilla, Parada, VigenciaD, VigenciaH 
                 FROM dbo.dist_CambiosParadasCanillas 
                 WHERE Id_Registro = @IdRegistro;";
      return await connection.QuerySingleOrDefaultAsync<CambioParadaCanilla>(sql, new { IdRegistro = idRegistro });
    }

    public async Task<CambioParadaCanilla?> GetCurrentParadaAsync(int idCanilla, IDbTransaction? transaction = null)
    {
      const string sql = @"
                 SELECT TOP 1 Id_Registro AS IdRegistro, Id_Canilla AS IdCanilla, Parada, VigenciaD, VigenciaH
                 FROM dbo.dist_CambiosParadasCanillas
                 WHERE Id_Canilla = @IdCanilla AND VigenciaH IS NULL
                 ORDER BY VigenciaD DESC, Id_Registro DESC;"; // Por si hay un error y quedaron varias abiertas

      var conn = transaction?.Connection ?? _cf.CreateConnection();
      bool manageConnection = transaction == null;
      if (manageConnection && conn.State != ConnectionState.Open) { if (conn is System.Data.Common.DbConnection dbConn) await dbConn.OpenAsync(); else conn.Open(); }
      try
      {
        return await conn.QuerySingleOrDefaultAsync<CambioParadaCanilla>(sql, new { IdCanilla = idCanilla }, transaction);
      }
      finally
      {
        if (manageConnection && conn.State == ConnectionState.Open) { if (conn is System.Data.Common.DbConnection dbConn) await dbConn.CloseAsync(); else conn.Close(); }
      }
    }


    public async Task<CambioParadaCanilla?> CreateAsync(CambioParadaCanilla nuevaParada, int idUsuario, IDbTransaction transaction)
    {
      const string sqlInsert = @"
                 INSERT INTO dbo.dist_CambiosParadasCanillas (Id_Canilla, Parada, VigenciaD, VigenciaH)
                 OUTPUT INSERTED.Id_Registro AS IdRegistro, INSERTED.Id_Canilla AS IdCanilla, INSERTED.Parada, INSERTED.VigenciaD, INSERTED.VigenciaH
                 VALUES (@IdCanilla, @Parada, @VigenciaD, @VigenciaH);";

      // VigenciaH es null al crear una nueva parada activa
      var inserted = await transaction.Connection!.QuerySingleAsync<CambioParadaCanilla>(sqlInsert,
          new { nuevaParada.IdCanilla, nuevaParada.Parada, nuevaParada.VigenciaD, VigenciaH = (DateTime?)null },
          transaction);

      if (inserted == null || inserted.IdRegistro == 0) throw new DataException("Error al crear cambio de parada o ID no generado.");

      await LogHistorialAsync(inserted, idUsuario, "Creada", transaction.Connection!, transaction);
      return inserted;
    }

    public async Task<bool> UpdateVigenciaHAsync(int idRegistro, DateTime vigenciaH, int idUsuario, IDbTransaction transaction)
{
    var paradaOriginal = await GetByIdAsync(idRegistro);
    if (paradaOriginal == null) throw new KeyNotFoundException("Registro de parada no encontrado para actualizar VigenciaH.");

    var paradaParaHistorial = new CambioParadaCanilla
    {
        IdRegistro = paradaOriginal.IdRegistro,
        IdCanilla = paradaOriginal.IdCanilla,
        Parada = paradaOriginal.Parada,
        VigenciaD = paradaOriginal.VigenciaD,
        VigenciaH = vigenciaH.Date
    };

    // Loggear el estado que QUEDARÁ en la tabla principal (con la VigenciaH actualizada)
    // El TipoMod debería reflejar la acción. "Cerrada".
    await LogHistorialAsync(paradaParaHistorial, idUsuario, "Cerrada", transaction.Connection!, transaction);

    const string sqlUpdate = @"
                 UPDATE dbo.dist_CambiosParadasCanillas 
                 SET VigenciaH = @VigenciaH
                 WHERE Id_Registro = @IdRegistro;";
    var rowsAffected = await transaction.Connection!.ExecuteAsync(sqlUpdate,
          new { VigenciaH = vigenciaH.Date, IdRegistro = idRegistro },
          transaction);

    return rowsAffected == 1;
}

    public async Task<bool> DeleteAsync(int idRegistro, int idUsuario, IDbTransaction transaction)
    {
      var paradaOriginal = await GetByIdAsync(idRegistro);
      if (paradaOriginal == null) throw new KeyNotFoundException("Registro de parada no encontrado para eliminar.");

      // Loggear ANTES de eliminar
      await LogHistorialAsync(paradaOriginal, idUsuario, "Eliminada", transaction.Connection!, transaction);

      const string sqlDelete = "DELETE FROM dbo.dist_CambiosParadasCanillas WHERE Id_Registro = @IdRegistro;";
      var rowsAffected = await transaction.Connection!.ExecuteAsync(sqlDelete, new { IdRegistro = idRegistro }, transaction);
      return rowsAffected == 1;
    }

    public async Task<IEnumerable<(CambioParadaCanillaHistorial Historial, string NombreUsuarioModifico, string NombreCanilla)>> GetCambiosParadaHistorialAsync(
        DateTime? fechaDesde, DateTime? fechaHasta,
        int? idUsuarioModifico, string? tipoModificacion,
        int? idCanillaOriginal)
        {
            using var connection = _cf.CreateConnection();
            var sqlBuilder = new StringBuilder(@"
            SELECT 
                h.Id_Registro, h.Id_Canilla, h.Parada, h.VigenciaD, h.VigenciaH,
                h.Id_Usuario, h.FechaMod, h.TipoMod,
                u.Nombre + ' ' + u.Apellido AS NombreUsuarioModifico,
                c.NomApe AS NombreCanilla
            FROM dbo.dist_CambiosParadasCanillas_H h
            JOIN dbo.gral_Usuarios u ON h.Id_Usuario = u.Id
            JOIN dbo.dist_dtCanillas c ON h.Id_Canilla = c.Id_Canilla
            WHERE 1=1");

            var parameters = new DynamicParameters();

            if (fechaDesde.HasValue) { sqlBuilder.Append(" AND h.FechaMod >= @FechaDesdeParam"); parameters.Add("FechaDesdeParam", fechaDesde.Value.Date); }
            if (fechaHasta.HasValue) { sqlBuilder.Append(" AND h.FechaMod <= @FechaHastaParam"); parameters.Add("FechaHastaParam", fechaHasta.Value.Date.AddDays(1).AddTicks(-1)); }
            if (idUsuarioModifico.HasValue) { sqlBuilder.Append(" AND h.Id_Usuario = @IdUsuarioModificoParam"); parameters.Add("IdUsuarioModificoParam", idUsuarioModifico.Value); }
            if (!string.IsNullOrWhiteSpace(tipoModificacion)) { sqlBuilder.Append(" AND h.TipoMod = @TipoModParam"); parameters.Add("TipoModParam", tipoModificacion); }
            if (idCanillaOriginal.HasValue) { sqlBuilder.Append(" AND h.Id_Canilla = @IdCanillaOriginalParam"); parameters.Add("IdCanillaOriginalParam", idCanillaOriginal.Value); }

            sqlBuilder.Append(" ORDER BY h.FechaMod DESC;");

            try
            {
                var result = await connection.QueryAsync<CambioParadaCanillaHistorial, string, string, (CambioParadaCanillaHistorial, string, string)>(
                    sqlBuilder.ToString(),
                    (hist, userMod, canillaNombre) => (hist, userMod, canillaNombre),
                    parameters,
                    splitOn: "NombreUsuarioModifico,NombreCanilla"
                );
                return result;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error al obtener historial de Cambios de Parada.");
                return Enumerable.Empty<(CambioParadaCanillaHistorial, string, string)>();
            }
        }
  }
}