using Asp.Versioning;
using FluentValidation;
using Hellang.Middleware.ProblemDetails;
using MyService.API.Application.Behaviors;
using MyService.Infrastructure;
using OpenTelemetry.Metrics;
using Serilog;

// EN: Configure Serilog early / VI: Cấu hình Serilog sớm
Log.Logger = new LoggerConfiguration()
    .WriteTo.Console()
    .CreateBootstrapLogger();

try
{
    Log.Information("Starting MyService API / Khởi động MyService API");

    var builder = WebApplication.CreateBuilder(args);

    // EN: Configure Serilog / VI: Cấu hình Serilog
    builder.Host.UseSerilog((context, services, configuration) => configuration
        .ReadFrom.Configuration(context.Configuration)
        .ReadFrom.Services(services)
        .Enrich.FromLogContext()
        .WriteTo.Console());

    // EN: Add Infrastructure services / VI: Thêm Infrastructure services
    builder.Services.AddInfrastructure(builder.Configuration);

    // 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) with domain exception mappings.
    //     All services MUST map their domain exceptions here so ProblemDetails middleware
    //     handles them before the generic 500 fallback.
    // VI: Thêm ProblemDetails middleware (RFC 7807) với domain exception mappings.
    //     Mọi service PHẢI map domain exceptions ở đây để ProblemDetails middleware
    //     xử lý chúng trước fallback 500 chung.
    builder.Services.AddProblemDetails(options =>
    {
        options.IncludeExceptionDetails = (ctx, ex) =>
            builder.Environment.IsDevelopment();

        // EN: Map FluentValidation.ValidationException to 400 BadRequest with field-level errors.
        // VI: Map FluentValidation.ValidationException sang 400 BadRequest với lỗi theo field.
        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 DomainException (base) to 422 Unprocessable Entity.
        //     Replace with your specific domain exception types:
        //     e.g. DuplicateResourceException -> 409, EntityNotFoundException -> 404
        // VI: Map DomainException (base) sang 422 Unprocessable Entity.
        //     Thay bằng các domain exception cụ thể của service:
        //     ví dụ DuplicateResourceException -> 409, EntityNotFoundException -> 404
        options.Map<MyService.Domain.Exceptions.DomainException>(ex =>
            new Microsoft.AspNetCore.Mvc.ProblemDetails
            {
                Title = "Business Rule Violation",
                Status = StatusCodes.Status422UnprocessableEntity,
                Detail = ex.Message,
                Type = "https://httpstatuses.io/422"
            });

        // EN: TODO — add service-specific mappings below following this pattern:
        //
        //   options.Map<MyService.Domain.Exceptions.EntityNotFoundException>(ex =>
        //       new ProblemDetails { Title = "Not Found", Status = 404, Detail = ex.Message,
        //                           Type = "https://httpstatuses.io/404" });
        //
        //   options.Map<MyService.Domain.Exceptions.DuplicateResourceException>(ex =>
        //       new ProblemDetails { Title = "Conflict", Status = 409, Detail = ex.Message,
        //                           Type = "https://httpstatuses.io/409" });
        //
        // VI: TODO — thêm mappings riêng cho từng service theo pattern này.
    });

    // EN: Add Swagger / VI: Thêm Swagger
    builder.Services.AddEndpointsApiExplorer();
    builder.Services.AddSwaggerGen(options =>
    {
        options.SwaggerDoc("v1", new()
        {
            Title = "MyService API",
            Version = "v1",
            Description = "MyService microservice API / API microservice MyService"
        });
    });

    // EN: Add health checks / VI: Thêm health checks
    builder.Services.AddHealthChecks()
        .AddNpgSql(
            builder.Configuration.GetConnectionString("DefaultConnection")
                ?? builder.Configuration["DATABASE_URL"]
                ?? "",
            name: "postgresql",
            tags: ["db", "postgresql"]);

    // EN: Add OpenTelemetry Metrics with Prometheus exporter.
    //     Exposes /metrics endpoint for Prometheus scraping.
    //     Includes ASP.NET Core request metrics and .NET runtime metrics.
    // VI: Thêm OpenTelemetry Metrics với Prometheus exporter.
    //     Expose endpoint /metrics để Prometheus scrape.
    //     Bao gồm metrics cho ASP.NET Core requests và .NET runtime.
    builder.Services.AddOpenTelemetry()
        .WithMetrics(metrics => metrics
            .AddAspNetCoreInstrumentation()
            .AddRuntimeInstrumentation()
            .AddPrometheusExporter());

    // EN: Add CORS — restrict to allowed origins. In production, set AllowedOrigins in config
    //     to ["https://goodgo.vn", "https://admin.goodgo.vn"]. Dev fallback is localhost only.
    //     NOTE: Do NOT use AllowAnyOrigin() in any environment — Traefik handles external traffic.
    // VI: Thêm CORS — giới hạn origins được phép. Trong production, đặt AllowedOrigins trong config
    //     thành ["https://goodgo.vn", "https://admin.goodgo.vn"]. Dev fallback chỉ localhost.
    //     LƯU Ý: KHÔNG dùng AllowAnyOrigin() trong bất kỳ môi trường nào — Traefik xử lý traffic ngoài.
    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: 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", "MyService API v1");
            c.RoutePrefix = "swagger";
        });
    }

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

    // EN: Map Prometheus metrics scraping endpoint.
    //     Accessible at /metrics — scraped by Prometheus every 15s.
    //     NOTE: This endpoint should be blocked at the API gateway for external traffic.
    // VI: Map endpoint scraping metrics Prometheus.
    //     Truy cập tại /metrics — Prometheus scrape mỗi 15 giây.
    //     LƯU Ý: Endpoint này nên bị chặn ở API gateway không cho traffic ngoài.
    app.MapPrometheusScrapingEndpoint("/metrics");

    // 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();
}
catch (Exception ex)
{
    Log.Fatal(ex, "Application terminated unexpectedly / Ứng dụng kết thúc bất ngờ");
    throw;
}
finally
{
    Log.CloseAndFlush();
}

// 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 { }
