using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Linq;
using bdAntifraude.dbcontext;
using System.Reflection;
using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations;
using System.Linq.Expressions;
using SwaggerAntifraude.DTOs;
using System.Linq.Dynamic.Core;
using bdAntifraude.db;
using System.Runtime.CompilerServices;
using Serialize.Linq.Serializers;


// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860

namespace SwaggerAntifraude.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public abstract class GenericoController<TEntity, TKey> : ControllerBase where TEntity : class
    {
        /// <summary>
        /// Obtiene todos los registros de la entidad (Solo Lectura).
        /// </summary>
        /// <returns>Lista de entidades.</returns>
        [Authorize(Policy = "LecturaPolicy")]
        [HttpGet]
        public virtual async Task<IActionResult> GetAll()
        {
            try
            {
                // Asegúrate de pasar UseLazyLoadingProxies: false para desactivar Lazy Loading
                using (var context = tsGestionAntifraude.NuevoContexto(SoloLectura: true, UseLazyLoadingProxies: false))
                {
                    var dbSet = context.Set<TEntity>();
                    var entities = await dbSet.AsNoTracking().ToListAsync();
                    //string jsonString = JsonSerializer.Serialize(entities);

                    return Ok(entities);
                }
            }
            catch (Exception ex)
            {
                return StatusCode(500, $"Error interno del servidor: {ex.Message}");
            }
        }

        /// <summary>
        /// Obtiene un registro específico por ID (Solo Lectura).
        /// </summary>
        /// <param name="id">ID de la entidad.</param>
        /// <returns>Entidad encontrada.</returns>
        [Authorize(Policy = "LecturaPolicy")]
        [HttpGet("{id}")]
        public virtual IActionResult GetById(TKey id)
        {
            try
            {
                using (var context = tsGestionAntifraude.NuevoContexto(SoloLectura: true, UseLazyLoadingProxies: false))
                {
                    var dbSet = context.Set<TEntity>();
                    var entity = dbSet.Find(id);

                    if (entity == null)
                        return NotFound();

                    return Ok(entity);
                }
            }
            catch (Exception ex)
            {
                return StatusCode(500, $"Error interno del servidor: {ex.Message}");
            }
        }



        [Authorize(Policy = "LecturaPolicy")]
        [HttpPost("filtrar")]
        public virtual async Task<IActionResult> Filtrar([FromBody] ExpressionWrapper request)
        {
            IQueryable<TEntity>? query;
            try
            {
                using (var context = tsGestionAntifraude.NuevoContexto(SoloLectura: true, UseLazyLoadingProxies: false))
                {
                    
                    
                    // Crear el deserializador
                    var serializer = new ExpressionSerializer(new Serialize.Linq.Serializers.JsonSerializer());

                    // Deserializar la expresión
                    var deserializedExpression = serializer.DeserializeText(request.Expression) as Expression<Func<TEntity, bool>>;
                    if (deserializedExpression == null)
                    {
                        return BadRequest("La expresión deserializada es nula o incorrecta.");
                    }

                    // Aplicar el filtro en la consulta
                    query = context.Set<TEntity>().Where(deserializedExpression);



                    // Agregar un log para verificar el SQL generado y la consulta
                    Console.WriteLine(query.ToQueryString());  // Esto imprime la consulta SQL generada por EF Core

                    var res = await query.AsNoTracking().ToListAsync();

                    // Verificar si el resultado es vacío
                    if (res.Count == 0)
                    {
                        Console.WriteLine("La consulta no devolvió resultados.");
                    }

                    return Ok(res);
                }
            }
               catch (Exception ex)
            {
                return StatusCode(500, $"Error interno del servidor: {ex.Message}");
            }
        }


        [Authorize(Policy = "LecturaPolicy")]
        [HttpPost("filtrar-entidad")]
        public virtual async Task<IActionResult> FiltrarEntidad([FromBody] ExpressionWrapper request)
        {
            try
            {
                using (var context = tsGestionAntifraude.NuevoContexto(SoloLectura: true, UseLazyLoadingProxies: false))
                {
                    var serializer = new ExpressionSerializer(new Serialize.Linq.Serializers.JsonSerializer());
                    var deserializedExpression = serializer.DeserializeText(request.Expression) as Expression<Func<TEntity, bool>>;

                    if (deserializedExpression == null)
                        return BadRequest("La expresión deserializada es nula o incorrecta.");

                    var entity = await context.Set<TEntity>()
                        .Where(deserializedExpression)
                        .AsNoTracking()
                        .FirstOrDefaultAsync();

                    if (entity == null)
                        return NotFound("No se encontró ninguna entidad que coincida con el filtro.");

                    return Ok(entity);
                }
            }
            catch (Exception ex)
            {
                return StatusCode(500, $"Error interno del servidor: {ex.Message}");
            }
        }

        [Authorize(Policy = "LecturaPolicy")]
        [HttpPost("filtrar-Atributo")]
        public virtual async Task<IActionResult> FiltrarAtributoDinamico([FromBody] ExpressionWrapperAtributo request)
        {
            try
            {
                using (var context = tsGestionAntifraude.NuevoContexto(SoloLectura: true, UseLazyLoadingProxies: false))
                {
                    var serializer = new ExpressionSerializer(new Serialize.Linq.Serializers.JsonSerializer());
                    var expression = serializer.DeserializeText(request.Expression) as Expression<Func<TEntity, bool>>;
                    if (expression == null)
                        return BadRequest("La expresión es nula o incorrecta.");

                    if (string.IsNullOrWhiteSpace(request.AttributeName))
                        return BadRequest("El nombre del atributo es obligatorio.");

                    var parameter = Expression.Parameter(typeof(TEntity), "x");
                    var property = Expression.PropertyOrField(parameter, request.AttributeName);

                    if (property == null)
                        return BadRequest($"El atributo '{request.AttributeName}' no existe en la entidad.");

                    // Crear una expresión para seleccionar el atributo
                    var attributeSelector = Expression.Lambda<Func<TEntity, object>>(
                    Expression.Convert(property, typeof(object)),
                    parameter);

                    var entities = await context.Set<TEntity>()
                    .Where(expression)
                    .Select(attributeSelector)
                    .AsNoTracking()
                    .ToListAsync();

                    return Ok(entities);
                }
            }
            catch (Exception ex)
            {
                return StatusCode(500, $"Error interno del servidor: {ex.Message}");
            }
        }




        /// <summary>
        /// Crea una nueva entidad (Escritura).
        /// </summary>
        /// <param name="entity">Entidad a crear.</param>
        /// <returns>Entidad creada.</returns>
        /// <summary>
        /// Crea una nueva entidad (Escritura).
        /// </summary>
        /// <param name="entity">Entidad a crear.</param>
        /// <returns>Entidad creada.</returns>
        [Authorize(Policy = "SupervisorPolicy")]
        [HttpPost]
        public virtual IActionResult Create([FromBody] TEntity entity)
        {
            // Validación automática: mantenemos tu comportamiento original
            if (!TryValidateModel(entity))
                ModelState.Clear();

            try
            {
                using var context = tsGestionAntifraude.NuevoContexto(
                    SoloLectura: false,
                    UseLazyLoadingProxies: false
                );
                var dbSet = context.Set<TEntity>();

                // 1) Creamos una instancia vacía y la añadimos al contexto
                var newEntity = Activator.CreateInstance<TEntity>();
                dbSet.Add(newEntity);

                // 2) Obtenemos el metadata de EF Core para TEntity
                var efEntityType = context.Model.FindEntityType(typeof(TEntity));
                if (efEntityType == null)
                    throw new InvalidOperationException($"No se encontró metadata EF para {typeof(TEntity).Name}");

                foreach (var prop in typeof(TEntity)
                                     .GetProperties(BindingFlags.Public | BindingFlags.Instance))
                {
                    // Solo las que EF Core mapea a columna
                    if (efEntityType.FindProperty(prop.Name) != null)
                    {
                        var valor = prop.GetValue(entity);
                        context.Entry(newEntity)
                               .Property(prop.Name)
                               .CurrentValue = valor;
                    }
                }

                // 4) Guardamos
                context.SaveChanges();

                // 5) Devolvemos CreatedAtAction con el ID recién generado
                var id = GetEntityId(newEntity);
                if (id != null)
                    return CreatedAtAction(nameof(GetById), new { id }, newEntity);

                return Ok(newEntity);
            }
            catch (Exception ex)
            {
                return StatusCode(500, $"Error interno del servidor: {ex.Message}");
            }
        }

        /// <summary>
        /// Actualiza una entidad existente (Escritura).
        /// </summary>
        /// <param name="id">ID de la entidad a actualizar.</param>
        /// <param name="entity">Entidad actualizada.</param>
        /// <returns>No content.</returns>
        [Authorize(Policy = "SupervisorPolicy")]
        [HttpPut("{id}")]
        public virtual IActionResult Update(TKey id, [FromBody] TEntity entity)
        {
            var entityId = GetEntityId(entity);
            if (entityId == null || !id.Equals((TKey)entityId))
                return BadRequest("El ID de la entidad no coincide con el ID de la URL.");

            try
            {
                using (var context = tsGestionAntifraude.NuevoContexto(SoloLectura: false, UseLazyLoadingProxies: false))
                {
                    var dbSet = context.Set<TEntity>();

                    // Buscar la entidad existente
                    var existingEntity = dbSet.Find(id);
                    if (existingEntity == null)
                        return NotFound("No se encontró la entidad para actualizar.");

                    // Iterar sobre las propiedades de la entidad
                    foreach (var property in typeof(TEntity).GetProperties())
                    {
                        // Verificar si la propiedad es escalar o navegación
                        var propertyMetadata = context.Model.FindEntityType(typeof(TEntity))?
                                                 .FindProperty(property.Name);

                        if (propertyMetadata == null)
                        {
                            // Es una propiedad de navegación, omitirla
                            continue;
                        }

                        // Manejar propiedades escalares
                        var newValue = property.GetValue(entity);
                        var oldValue = property.GetValue(existingEntity);

                        if (!Equals(newValue, oldValue))
                        {
                            context.Entry(existingEntity).Property(property.Name).CurrentValue = newValue;
                        }
                    }

                    // Guardar cambios
                    context.SaveChanges();
                }

                return NoContent();
            }
            catch (Exception ex)
            {
                return StatusCode(500, $"Error interno del servidor: {ex.Message}");
            }
        }

        /// <summary>
        /// Elimina una entidad por ID (Escritura).
        /// </summary>
        /// <param name="id">ID de la entidad a eliminar.</param>
        /// <returns>No content.</returns>
        [Authorize(Policy = "SupervisorPolicy")]
        [HttpDelete("{id}")]
        public virtual IActionResult Delete(TKey id)
        {
            using (var context = tsGestionAntifraude.NuevoContexto(SoloLectura: false, UseLazyLoadingProxies: false))
            {
                var dbSet = context.Set<TEntity>();
                var entity = dbSet.Find(id);

                if (entity == null)
                    return NotFound();

                dbSet.Remove(entity);
                context.SaveChanges();
            }

            return NoContent();
        }

        /// <summary>
        /// Obtiene el valor de la propiedad de ID de la entidad.
        /// </summary>
        /// <param name="entity">Entidad de la cual obtener el ID.</param>
        /// <returns>Valor del ID.</returns>
        protected virtual object GetEntityId(TEntity entity)
        {
            // Busca las propiedades que podrían ser claves primarias según las convenciones o el atributo [Key]
            var type = typeof(TEntity);

            // Primero, buscamos cualquier propiedad marcada con [Key], que es la forma más confiable de identificar el ID
            var idProperty = type.GetProperties().FirstOrDefault(p => p.GetCustomAttribute<KeyAttribute>() != null);

            if (idProperty != null)
            {
                return idProperty.GetValue(entity);
            }

            // Si no hay un atributo [Key], intentamos con algunas convenciones de nombres comunes para ID
            idProperty = type.GetProperties().FirstOrDefault(p =>
                p.Name.Equals("ID", StringComparison.OrdinalIgnoreCase) ||  // Nombre genérico 'Id'
                p.Name.Equals($"{type.Name}ID", StringComparison.OrdinalIgnoreCase) || //
                p.Name.StartsWith("ID", StringComparison.OrdinalIgnoreCase)); // Cualquier nombre que comience con 'ID'

            if (idProperty != null)
            {
                return idProperty.GetValue(entity);
            }

            throw new InvalidOperationException("No se pudo encontrar una propiedad de ID para la entidad.");
        }

    }
}

public class ExpressionWrapper
{
    public string Expression { get; set; }
}


public class ExpressionWrapperAtributo
{
    public string Expression { get; set; }
    public string AttributeName { get; set; }
}