using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.EntityFrameworkCore;
using WorkClub.Api.Auth;
using WorkClub.Api.Endpoints.Clubs;
using WorkClub.Api.Endpoints.Members;
using WorkClub.Api.Endpoints.Shifts;
using WorkClub.Api.Endpoints.Tasks;
using WorkClub.Api.Middleware;
using WorkClub.Api.Services;
using WorkClub.Application.Interfaces;
using WorkClub.Infrastructure.Data;
using WorkClub.Infrastructure.Data.Interceptors;
using WorkClub.Infrastructure.Services;
using WorkClub.Infrastructure.Seed;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddOpenApi();

builder.Services.AddHttpContextAccessor();
builder.Services.AddScoped<ITenantProvider, TenantProvider>();
builder.Services.AddScoped<SeedDataService>();
builder.Services.AddScoped<TaskService>();
builder.Services.AddScoped<ShiftService>();
builder.Services.AddScoped<ClubService>();
builder.Services.AddScoped<AdminClubService>();
builder.Services.AddScoped<MemberService>();
builder.Services.AddScoped<MemberSyncService>();

builder.Services.AddScoped<TenantDbTransactionInterceptor>();
builder.Services.AddSingleton<SaveChangesTenantInterceptor>();

// Add CORS to allow frontend requests
builder.Services.AddCors(options =>
{
    options.AddPolicy("AllowFrontend", policy =>
    {
        policy.WithOrigins("http://localhost:3000")
              .AllowAnyHeader()
              .AllowAnyMethod()
              .AllowCredentials();
    });
});

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.Authority = builder.Configuration["Keycloak:Authority"];
        options.Audience = builder.Configuration["Keycloak:Audience"];
        options.RequireHttpsMetadata = false;
        options.MapInboundClaims = false;
        options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
        {
            ValidateIssuer = false, // Disabled for local dev - external clients use localhost:8080, internal use keycloak:8080
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true
        };
        options.Events = new JwtBearerEvents
        {
            OnAuthenticationFailed = context =>
            {
                Console.WriteLine($"JWT Authentication Failed: {context.Exception.Message}");
                if (context.Exception.InnerException != null)
                {
                    Console.WriteLine($"Inner Exception: {context.Exception.InnerException.Message}");
                }
                return Task.CompletedTask;
            },
            OnTokenValidated = context =>
            {
                Console.WriteLine($"JWT Token Validated for user: {context.Principal?.Identity?.Name ?? "unknown"}");
                return Task.CompletedTask;
            },
            OnChallenge = context =>
            {
                Console.WriteLine($"JWT Challenge: {context.Error}");
                return Task.CompletedTask;
            }
        };
    });

builder.Services.AddScoped<IClaimsTransformation, ClubRoleClaimsTransformation>();

builder.Services.AddAuthorizationBuilder()
    .AddPolicy("RequireGlobalAdmin", policy => policy.RequireAssertion(context =>
    {
        var realmAccess = context.User.FindFirst("realm_access")?.Value;
        if (string.IsNullOrEmpty(realmAccess))
            return false;

        try
        {
            using var doc = System.Text.Json.JsonDocument.Parse(realmAccess);
            if (doc.RootElement.TryGetProperty("roles", out var rolesElement) &&
                rolesElement.ValueKind == System.Text.Json.JsonValueKind.Array)
            {
                foreach (var role in rolesElement.EnumerateArray())
                {
                    if (role.GetString() == "admin")
                        return true;
                }
            }
        }
        catch
        {
            // If JSON parsing fails, fallback to string contains check
            return realmAccess.Contains("admin", StringComparison.OrdinalIgnoreCase);
        }

        return false;
    }))
    .AddPolicy("RequireManager", policy => policy.RequireRole("Manager"))
    .AddPolicy("RequireMember", policy => policy.RequireRole("Manager", "Member"))
    .AddPolicy("RequireViewer", policy => policy.RequireAuthenticatedUser());

builder.Services.AddDbContext<AppDbContext>((sp, options) =>
    options.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection"))
           .AddInterceptors(
               sp.GetRequiredService<TenantDbTransactionInterceptor>(),
               sp.GetRequiredService<SaveChangesTenantInterceptor>()));

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
if (!string.IsNullOrEmpty(connectionString))
{
    builder.Services.AddHealthChecks()
        .AddNpgSql(connectionString);
}
else
{
    builder.Services.AddHealthChecks();
}

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.MapOpenApi();

    using var scope = app.Services.CreateScope();
    var seedService = scope.ServiceProvider.GetRequiredService<SeedDataService>();
    await seedService.SeedAsync();
}

app.UseHttpsRedirection();

app.UseCors("AllowFrontend");

app.UseAuthentication();
app.UseAuthorization();
app.UseMiddleware<TenantValidationMiddleware>();
app.UseMiddleware<MemberSyncMiddleware>();

app.MapHealthChecks("/health/live", new Microsoft.AspNetCore.Diagnostics.HealthChecks.HealthCheckOptions
{
    Predicate = _ => false
});

app.MapHealthChecks("/health/ready");
app.MapHealthChecks("/health/startup");

var summaries = new[]
{
    "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};

app.MapGet("/weatherforecast", () =>
{
    var forecast = Enumerable.Range(1, 5).Select(index =>
        new WeatherForecast
        (
            DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
            Random.Shared.Next(-20, 55),
            summaries[Random.Shared.Next(summaries.Length)]
        ))
        .ToArray();
    return forecast;
})
.WithName("GetWeatherForecast");

app.MapGet("/api/debug/claims", (HttpContext context) =>
{
    var claims = context.User.Claims.Select(c => new { c.Type, c.Value }).ToList();
    var realmAccess = context.User.FindFirst("realm_access")?.Value;
    
    // Check if the authorization header is present
    var authHeader = context.Request.Headers["Authorization"].FirstOrDefault();
    
    return Results.Ok(new 
    { 
        isAuthenticated = context.User.Identity?.IsAuthenticated ?? false,
        authenticationType = context.User.Identity?.AuthenticationType,
        claimCount = claims.Count,
        claims = claims,
        realmAccess = realmAccess,
        hasAuthHeader = !string.IsNullOrEmpty(authHeader),
        authHeaderPrefix = authHeader?.Substring(0, Math.Min(20, authHeader?.Length ?? 0))
    });
});

app.MapTaskEndpoints();
app.MapShiftEndpoints();
app.MapClubEndpoints();
app.MapAdminClubEndpoints();
app.MapMemberEndpoints();

app.Run();

record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
{
    public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}

public partial class Program { }
