using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using System.Reflection;
using System.Text;
using System.Text.Json;
using MyNewProjectName.Infrastructure.Options;
using MyNewProjectName.Contracts.Common;

namespace MyNewProjectName.WebAPI.Extensions;

/// <summary>
/// Extension methods for configuring services in the dependency injection container
/// </summary>
public static class ServiceCollectionExtensions
{
    /// <summary>
    /// Configures Swagger/OpenAPI documentation with JWT Bearer authentication support
    /// </summary>
    public static IServiceCollection AddCustomSwagger(this IServiceCollection services)
    {
        services.AddSwaggerGen(c =>
        {
            c.SwaggerDoc("v1", new OpenApiInfo { Title = "MyNewProjectName API", Version = "v1" });

            var securityScheme = new OpenApiSecurityScheme
            {
                Name = "Authorization",
                Description = "Enter JWT Bearer token",
                In = ParameterLocation.Header,
                Type = SecuritySchemeType.Http,
                Scheme = "bearer",
                BearerFormat = "JWT"
            };
            c.AddSecurityDefinition("Bearer", securityScheme);

            var securityRequirement = new OpenApiSecurityRequirement
            {
                {
                    new OpenApiSecurityScheme
                    {
                        Reference = new OpenApiReference
                        {
                            Type = ReferenceType.SecurityScheme,
                            Id = "Bearer"
                        }
                    },
                    new string[] {}
                }
            };
            c.AddSecurityRequirement(securityRequirement);

            // Set the comments path for the Swagger JSON and UI (only if XML file exists)
            var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
            var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
            if (File.Exists(xmlPath))
            {
                c.IncludeXmlComments(xmlPath);
            }
        });

        return services;
    }

    /// <summary>
    /// Configures JWT Bearer authentication with custom token validation and error handling
    /// </summary>
    public static IServiceCollection AddJwtAuthentication(this IServiceCollection services, IConfiguration configuration)
    {
        var jwtOptionsSection = configuration.GetSection(JwtOptions.SectionName);
        var jwtOptions = jwtOptionsSection.Get<JwtOptions>() 
            ?? throw new InvalidOperationException("JwtOptions configuration is missing");

        services
            .AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            })
            .AddJwtBearer(options =>
            {
                options.RequireHttpsMetadata = false;
                options.SaveToken = true;
                options.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuer = true,
                    ValidateAudience = true,
                    ValidateIssuerSigningKey = true,
                    ValidateLifetime = true,
                    ValidIssuer = jwtOptions.Issuer,
                    ValidAudience = jwtOptions.Audience,
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtOptions.Secret)),
                    ClockSkew = TimeSpan.Zero
                };

                // Log token & claims for debugging
                options.Events = new JwtBearerEvents
                {
                    OnMessageReceived = context =>
                    {
                        var logger = context.HttpContext.RequestServices
                            .GetRequiredService<ILoggerFactory>()
                            .CreateLogger("JwtBearer");

                        // Có thể bật lại nếu cần debug Authorization header
                        // logger.LogInformation("JWT received. Authorization header: {Header}", context.Request.Headers["Authorization"].ToString());
                        return Task.CompletedTask;
                    },
                    OnTokenValidated = context =>
                    {
                        var logger = context.HttpContext.RequestServices
                            .GetRequiredService<ILoggerFactory>()
                            .CreateLogger("JwtBearer");

                        var claims = context.Principal?.Claims
                            .Select(c => $"{c.Type}={c.Value}")
                            .ToList() ?? new List<string>();

                        // logger.LogInformation("JWT validated. Claims: {Claims}", string.Join(", ", claims));
                        return Task.CompletedTask;
                    },
                    OnAuthenticationFailed = context =>
                    {
                        var logger = context.HttpContext.RequestServices
                            .GetRequiredService<ILoggerFactory>()
                            .CreateLogger("JwtBearer");

                        logger.LogWarning(context.Exception, "JWT authentication failed");
                        return Task.CompletedTask;
                    },
                    OnChallenge = async context =>
                    {
                        // Ngăn không cho middleware mặc định ghi response lần nữa
                        context.HandleResponse();

                        var logger = context.HttpContext.RequestServices
                            .GetRequiredService<ILoggerFactory>()
                            .CreateLogger("JwtBearer");

                        if (context.Response.HasStarted)
                        {
                            logger.LogWarning("The response has already started, the JWT challenge middleware will not be executed.");
                            return;
                        }

                        var correlationId = context.HttpContext.Request.Headers["X-Correlation-ID"].FirstOrDefault() ?? "unknown";

                        var isExpired = context.AuthenticateFailure is SecurityTokenExpiredException;
                        var message = isExpired
                            ? "Token expired"
                            : "Unauthorized";

                        logger.LogInformation("JWT challenge. Expired: {Expired}, Error: {Error}, Description: {Description}, CorrelationId: {CorrelationId}",
                            isExpired, context.Error, context.ErrorDescription, correlationId);

                        context.Response.StatusCode = StatusCodes.Status401Unauthorized;
                        context.Response.ContentType = "application/json";

                        var response = new ServiceResponse<object>
                        {
                            Success = false,
                            Message = message,
                            Errors = null
                        };

                        var json = JsonSerializer.Serialize(response, new JsonSerializerOptions
                        {
                            PropertyNamingPolicy = JsonNamingPolicy.CamelCase
                        });

                        await context.Response.WriteAsync(json);
                    },
                    OnForbidden = async context =>
                    {
                        var logger = context.HttpContext.RequestServices
                            .GetRequiredService<ILoggerFactory>()
                            .CreateLogger("JwtBearer");

                        logger.LogWarning("User is authenticated but does not have access to the requested resource (403 Forbidden).");

                        context.Response.StatusCode = StatusCodes.Status403Forbidden;
                        context.Response.ContentType = "application/json";

                        var response = new ServiceResponse<object>
                        {
                            Success = false,
                            Message = "You do not have permission to perform this action.",
                            Errors = null
                        };

                        var json = JsonSerializer.Serialize(response, new JsonSerializerOptions
                        {
                            PropertyNamingPolicy = JsonNamingPolicy.CamelCase
                        });

                        await context.Response.WriteAsync(json);
                    }
                };
            });

        return services;
    }

    // /// <summary>
    // /// Configures authorization policies based on user type and roles.
    // /// </summary>
    // public static IServiceCollection AddCustomAuthorization(this IServiceCollection services)
    // {
    //     services.AddAuthorization(options =>
    //     {
    //         // 1. Policy chỉ dành cho SuperAdmin
    //         options.AddPolicy("RequireSuperAdmin", policy =>
    //             policy.RequireClaim("userType", "SuperAdmin"));

    //         // 2. Policy cho TenantAdmin (TenantMember + Role = TenantAdmin)
    //         options.AddPolicy("RequireTenantAdmin", policy =>
    //             policy.RequireClaim("userType", "TenantMember")
    //                   .RequireRole("TenantAdmin"));

    //         // 3. Policy cho TenantMember (TenantMember + Role = Doctor)
    //         options.AddPolicy("RequireTenantMember", policy =>
    //             policy.RequireClaim("userType", "TenantMember")
    //         );
    //         // 4. Policy SuperAdmin hoặc TenantAdmin
    //         options.AddPolicy("RequireSuperAdminOrTenantAdmin", policy => 
    //             policy.RequireAssertion(context => 
    //                 // Điều kiện 1: Là SuperAdmin
    //                 context.User.HasClaim("userType", "SuperAdmin") 
    //                 || 
    //                 // HOẶC Điều kiện 2: Là Nhân viên VÀ có Role là TenantAdmin
    //                 (context.User.HasClaim("userType", "TenantMember") && context.User.IsInRole("TenantAdmin"))
    //             ));
    //     });

    //     return services;
    // }
}
