using Asp.Versioning;
using Microsoft.EntityFrameworkCore;
using FluentValidation;
using Hellang.Middleware.ProblemDetails;
using IamService.API.Application.Services;
using IamService.API.Application.Behaviors;
using IamService.API.Swagger;
using IamService.Infrastructure;
using IamService.Infrastructure.Authorization;
using Serilog;

var builder = WebApplication.CreateBuilder(args);

// EN: Configure Serilog with fresh logger for each host (compatible with WebApplicationFactory)
// VI: Cấu hình Serilog với logger mới cho mỗi host (tương thích với WebApplicationFactory)
builder.Logging.ClearProviders();
builder.Host.UseSerilog((context, services, configuration) => configuration
    .ReadFrom.Configuration(context.Configuration)
    .ReadFrom.Services(services)
    .Enrich.FromLogContext()
    .WriteTo.Console());

// EN: Add Infrastructure services (Identity, Duende IdentityServer, Repositories)
// VI: Thêm Infrastructure services (Identity, Duende IdentityServer, Repositories)
builder.Services.AddInfrastructure(builder.Configuration, builder.Environment.EnvironmentName);
builder.Services.AddScoped<IVerificationOtpDispatcher, VerificationOtpDispatcher>();

// EN: Add Authorization Policies for Backoffice APIs
// VI: Thêm Authorization Policies cho các API Backoffice
builder.Services.AddHttpContextAccessor();
builder.Services.AddAuthorizationPolicies();

// EN: Add MediatR with behaviors / VI: Thêm MediatR với behaviors
builder.Services.AddMediatR(cfg =>
{
    cfg.RegisterServicesFromAssemblyContaining<Program>();
    cfg.AddOpenBehavior(typeof(LoggingBehavior<,>));
    cfg.AddOpenBehavior(typeof(ValidatorBehavior<,>));
    cfg.AddOpenBehavior(typeof(TransactionBehavior<,>));
});

// EN: Add FluentValidation / VI: Thêm FluentValidation
builder.Services.AddValidatorsFromAssemblyContaining<Program>();

// EN: Add API versioning / VI: Thêm API versioning
builder.Services.AddApiVersioning(options =>
{
    options.DefaultApiVersion = new ApiVersion(1, 0);
    options.AssumeDefaultVersionWhenUnspecified = true;
    options.ReportApiVersions = true;
    options.ApiVersionReader = ApiVersionReader.Combine(
        new UrlSegmentApiVersionReader(),
        new HeaderApiVersionReader("X-Api-Version"));
})
.AddApiExplorer(options =>
{
    options.GroupNameFormat = "'v'VVV";
    options.SubstituteApiVersionInUrl = true;
});

// EN: Add controllers / VI: Thêm controllers
builder.Services.AddControllers();

// EN: Add ProblemDetails middleware (RFC 7807) / VI: Thêm ProblemDetails middleware
builder.Services.AddProblemDetails(options =>
{
    options.IncludeExceptionDetails = (ctx, ex) =>
        builder.Environment.IsDevelopment();

    // EN: Map FluentValidation.ValidationException to 400 BadRequest
    // VI: Map FluentValidation.ValidationException sang 400 BadRequest
    options.Map<FluentValidation.ValidationException>(ex =>
    {
        var errors = ex.Errors
            .GroupBy(e => e.PropertyName)
            .ToDictionary(
                g => g.Key,
                g => g.Select(e => e.ErrorMessage).ToArray()
            );

        return new Microsoft.AspNetCore.Mvc.ValidationProblemDetails(errors)
        {
            Title = "Validation Error",
            Status = StatusCodes.Status400BadRequest,
            Detail = "One or more validation errors occurred.",
            Type = "https://httpstatuses.io/400"
        };
    });

    // EN: Map DuplicateResourceException to 409 Conflict
    // VI: Map DuplicateResourceException sang 409 Conflict
    options.Map<IamService.Domain.Exceptions.DuplicateResourceException>(ex =>
        new Microsoft.AspNetCore.Mvc.ProblemDetails
        {
            Title = "Conflict",
            Status = StatusCodes.Status409Conflict,
            Detail = ex.Message,
            Type = "https://httpstatuses.io/409"
        });

    // EN: Map EntityNotFoundException to 404 NotFound
    // VI: Map EntityNotFoundException sang 404 NotFound
    options.Map<IamService.Domain.Exceptions.EntityNotFoundException>(ex =>
        new Microsoft.AspNetCore.Mvc.ProblemDetails
        {
            Title = "Not Found",
            Status = StatusCodes.Status404NotFound,
            Detail = ex.Message,
            Type = "https://httpstatuses.io/404"
        });

    // EN: Map AuthenticationFailedException to 401 Unauthorized
    // VI: Map AuthenticationFailedException sang 401 Unauthorized
    options.Map<IamService.Domain.Exceptions.AuthenticationFailedException>(ex =>
        new Microsoft.AspNetCore.Mvc.ProblemDetails
        {
            Title = "Unauthorized",
            Status = StatusCodes.Status401Unauthorized,
            Detail = ex.Message,
            Type = "https://httpstatuses.io/401"
        });

    // EN: Map BusinessRuleException to 422 Unprocessable Entity
    // VI: Map BusinessRuleException sang 422 Unprocessable Entity
    options.Map<IamService.Domain.Exceptions.BusinessRuleException>(ex =>
        new Microsoft.AspNetCore.Mvc.ProblemDetails
        {
            Title = "Business Rule Violation",
            Status = StatusCodes.Status422UnprocessableEntity,
            Detail = ex.Message,
            Type = "https://httpstatuses.io/422"
        });
});

// EN: Add Swagger / VI: Thêm Swagger
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(options =>
{
    options.SwaggerDoc("v1", new()
    {
        Title = "IAM Service API",
        Version = "v1",
        Description = """
            Identity and Access Management Service - OAuth2/OIDC API
            
            ## Authentication
            This API uses OAuth2 with Password Grant and JWT Bearer tokens.
            
            ## Endpoints
            - **/api/v1/auth/register** - Register a new user
            - **/connect/token** - OAuth2 token endpoint
            - **/api/v1/users** - User management (requires authentication)
            """,
        Contact = new()
        {
            Name = "GoodGo Team",
            Email = "support@goodgo.com",
            Url = new Uri("https://github.com/goodgo")
        },
        License = new()
        {
            Name = "MIT License",
            Url = new Uri("https://opensource.org/licenses/MIT")
        }
    });

    // EN: Include XML comments for better documentation
    // VI: Include XML comments để documentation tốt hơn
    var xmlFilename = $"{System.Reflection.Assembly.GetExecutingAssembly().GetName().Name}.xml";
    var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFilename);
    if (File.Exists(xmlPath))
    {
        options.IncludeXmlComments(xmlPath, includeControllerXmlComments: true);
    }

    // EN: Add OAuth2 security definition / VI: Thêm OAuth2 security definition
    options.AddSecurityDefinition("oauth2", new()
    {
        Type = Microsoft.OpenApi.Models.SecuritySchemeType.OAuth2,
        Description = "OAuth2 Password Grant flow. Use email as username.",
        Flows = new()
        {
            Password = new()
            {
                TokenUrl = new Uri("/connect/token", UriKind.Relative),
                Scopes = new Dictionary<string, string>
                {
                    ["openid"] = "OpenID - Required for authentication",
                    ["profile"] = "Profile - Access to user profile information",
                    ["email"] = "Email - Access to user email",
                    ["roles"] = "Roles - Access to user roles",
                    ["api"] = "API - Full API access"
                }
            }
        }
    });

    // EN: Add JWT Bearer security definition / VI: Thêm JWT Bearer security definition
    options.AddSecurityDefinition("Bearer", new()
    {
        Type = Microsoft.OpenApi.Models.SecuritySchemeType.Http,
        Scheme = "bearer",
        BearerFormat = "JWT",
        Description = "JWT Authorization header using the Bearer scheme. Example: 'Bearer {token}'"
    });

    options.AddSecurityRequirement(new()
    {
        {
            new() { Reference = new() { Type = Microsoft.OpenApi.Models.ReferenceType.SecurityScheme, Id = "oauth2" } },
            ["api"]
        },
        {
            new() { Reference = new() { Type = Microsoft.OpenApi.Models.ReferenceType.SecurityScheme, Id = "Bearer" } },
            Array.Empty<string>()
        }
    });

    // EN: Enable annotations / VI: Bật annotations
    options.EnableAnnotations();
    
    // EN: Add /connect/token endpoint to Swagger / VI: Thêm /connect/token endpoint vào Swagger
    options.DocumentFilter<TokenEndpointDocumentFilter>();
});

// EN: Add health checks / VI: Thêm health checks
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection")
    ?? builder.Configuration["DATABASE_URL"]
    ?? "";

if (!string.IsNullOrEmpty(connectionString))
{
    builder.Services.AddHealthChecks()
        .AddNpgSql(connectionString, name: "postgresql", tags: ["db", "postgresql"]);
}
else
{
    builder.Services.AddHealthChecks();
}

// EN: Add CORS / VI: Thêm CORS
builder.Services.AddCors(options =>
{
    options.AddDefaultPolicy(policy =>
    {
        policy.WithOrigins(
                  builder.Configuration.GetSection("AllowedOrigins").Get<string[]>()
                  ?? ["http://localhost:3000", "http://localhost:5173", "http://localhost:5000"])
              .AllowAnyMethod()
              .AllowAnyHeader();
    });
});

var app = builder.Build();

// EN: Log startup message using ILogger
// VI: Log thông báo khởi động sử dụng ILogger
var logger = app.Services.GetRequiredService<ILogger<Program>>();
logger.LogInformation("Starting IAM Service API / Khởi động IAM Service API");

// EN: Auto-apply EF Core migrations on startup (Development only)
// VI: Tự động áp dụng EF Core migrations khi khởi động (chỉ Development)
if (app.Environment.IsDevelopment())
{
    using var scope = app.Services.CreateScope();
    var dbContext = scope.ServiceProvider.GetRequiredService<IamService.Infrastructure.IamServiceContext>();
    logger.LogInformation("Applying EF Core migrations...");
    await dbContext.Database.MigrateAsync();
    logger.LogInformation("EF Core migrations applied successfully.");
}

// EN: Seed system roles on startup
// VI: Seed system roles khi khởi động
try
{
    await IamService.Infrastructure.Data.DataSeeder.SeedRolesAsync(app.Services);
    logger.LogInformation("System roles seeding completed successfully.");
}
catch (Exception ex)
{
    logger.LogError(ex, "Failed to seed system roles: {Message}", ex.Message);
}

// EN: Configure middleware pipeline / VI: Cấu hình middleware pipeline
app.UseSerilogRequestLogging();
app.UseProblemDetails();

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI(c =>
    {
        c.SwaggerEndpoint("/swagger/v1/swagger.json", "IAM Service API v1");
        c.RoutePrefix = "swagger";
        c.OAuthClientId("swagger-ui");
        c.OAuthUsePkce();
    });
}

app.UseCors();
app.UseRouting();

// EN: Authentication and Authorization / VI: Xác thực và phân quyền
app.UseIdentityServer();
app.UseAuthentication();
app.UseAuthorization();

// EN: Map health check endpoints / VI: Map health check endpoints
app.MapHealthChecks("/health");
app.MapHealthChecks("/health/live", new()
{
    Predicate = _ => false // EN: Just checks app is running / VI: Chỉ kiểm tra app đang chạy
});
app.MapHealthChecks("/health/ready");

// EN: Map controllers / VI: Map controllers
app.MapControllers();

// EN: Run the application / VI: Chạy ứng dụng
app.Run();

// EN: Make Program class accessible for integration tests
// VI: Làm cho class Program có thể truy cập cho integration tests
public partial class Program { }
