using Elecciones.Database;
using Microsoft.EntityFrameworkCore;
using Serilog;
using Elecciones.Core.Services;
using Elecciones.Infrastructure.Services;
using System.Text;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using Elecciones.Database.Entities;
using System.Text.Json.Serialization;
using Microsoft.AspNetCore.HttpOverrides;

// Esta es la estructura estándar y recomendada.
var builder = WebApplication.CreateBuilder(args);

// 1. Configurar Serilog. Esta es la forma correcta de integrarlo.
builder.Host.UseSerilog((context, services, configuration) => configuration
    .ReadFrom.Configuration(context.Configuration)
    .ReadFrom.Services(services)
    .Enrich.FromLogContext()
    .WriteTo.Console()
    .WriteTo.File("logs/api-.log", rollingInterval: RollingInterval.Day));

// 2. Añadir servicios al contenedor.
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<EleccionesDbContext>(options =>
    options.UseSqlServer(connectionString));

builder.Services.AddScoped<IPasswordHasher, PasswordHasher>();

builder.Services.AddControllers().AddJsonOptions(options =>
{
    // Esto le dice al serializador que maneje las referencias circulares
    options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles;
});

var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
builder.Services.AddCors(options =>
{
    options.AddPolicy(name: MyAllowSpecificOrigins,
                      policy =>
                      {
                          policy.WithOrigins(
                              "http://localhost:5173", // Para widgets
                              "http://localhost:5174",
                              "https://www.eldia.com",
                              "https://extras.eldia.com"  // Para admin
                          )
                          .AllowAnyHeader()
                          .AllowAnyMethod();
                      });
});

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = builder.Configuration["Jwt:Issuer"],
            ValidAudience = builder.Configuration["Jwt:Audience"],
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]!))
        };
    });

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();


builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
    options.ForwardedHeaders =
        ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
    options.KnownNetworks.Clear();
    options.KnownProxies.Clear();
});

// 3. Construir la aplicación.
var app = builder.Build();

app.UseForwardedHeaders();

// Seeder para el usuario admin
using (var scope = app.Services.CreateScope())
{
    var services = scope.ServiceProvider;
    try
    {
        var context = services.GetRequiredService<EleccionesDbContext>();
        var hasher = services.GetRequiredService<IPasswordHasher>();
        if (!context.AdminUsers.Any())
        {
            var (hash, salt) = hasher.HashPassword("PTP847elec");
            context.AdminUsers.Add(new Elecciones.Database.Entities.AdminUser
            {
                Username = "admin",
                PasswordHash = hash,
                PasswordSalt = salt
            });
            context.SaveChanges();
            Console.WriteLine("--> Admin user seeded.");
        }
    }
    catch (Exception ex)
    {
        var logger = services.GetRequiredService<ILogger<Program>>();
        logger.LogError(ex, "An error occurred while seeding the database.");
    }
}

// Seeder para las bancas vacías
using (var scope = app.Services.CreateScope())
{
    var services = scope.ServiceProvider;
    var context = services.GetRequiredService<EleccionesDbContext>();
    if (!context.Bancadas.Any())
    {
        var bancas = new List<Bancada>();
        // 92 bancas de diputados
        for (int i = 1; i <= 92; i++) // Bucle de 1 a 92
        {
            bancas.Add(new Bancada
            {
                Camara = Elecciones.Core.Enums.TipoCamara.Diputados,
                NumeroBanca = i // Asignamos el número de banca
            });
        }
        // 46 bancas de senadores
        for (int i = 1; i <= 46; i++) // Bucle de 1 a 46
        {
            bancas.Add(new Bancada
            {
                Camara = Elecciones.Core.Enums.TipoCamara.Senadores,
                NumeroBanca = i // Asignamos el número de banca
            });
        }
        context.Bancadas.AddRange(bancas);
        context.SaveChanges();
        Console.WriteLine("--> Seeded 138 bancas físicas.");
    }
}

// Seeder para las configuraciones por defecto
using (var scope = app.Services.CreateScope())
{
    var services = scope.ServiceProvider;
    var context = services.GetRequiredService<EleccionesDbContext>();
    if (!context.Configuraciones.Any(c => c.Clave == "MostrarOcupantes"))
    {
        context.Configuraciones.Add(new Configuracion { Clave = "MostrarOcupantes", Valor = "true" });
        context.SaveChanges();
        Console.WriteLine("--> Seeded default configuration 'MostrarOcupantes'.");
    }
    if (!context.Configuraciones.Any(c => c.Clave == "TickerResultadosCantidad"))
    {
        context.Configuraciones.Add(new Configuracion { Clave = "TickerResultadosCantidad", Valor = "3" });
        context.Configuraciones.Add(new Configuracion { Clave = "ConcejalesResultadosCantidad", Valor = "5" });
        context.SaveChanges();
        Console.WriteLine("--> Seeded default configuration 'TickerResultadosCantidad'.");
    }
}

// Configurar el pipeline de peticiones HTTP.
// Añadimos el logging de peticiones de Serilog aquí.
app.UseSerilogRequestLogging();

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

// 1. Redirección a HTTPS (si se usa)
//app.UseHttpsRedirection();

// 2. Middleware de Enrutamiento. ¡CLAVE!
//    Determina a qué endpoint irá la petición.
app.UseRouting();

// 3. Middleware de CORS.
//    Ahora se ejecuta sabiendo a qué endpoint se dirige la petición.
app.UseCors(MyAllowSpecificOrigins);

// 4. Middleware de Autenticación.
//    Identifica quién es el usuario a partir del token.
app.UseAuthentication();

// 5. Middleware de Autorización.
//    Verifica si el usuario identificado tiene permiso para acceder al endpoint.
app.UseAuthorization();

// 6. Mapea los controladores a los endpoints que el enrutador descubrió.
app.MapControllers();

// 5. Ejecutar la aplicación.
app.Run();