using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Channels;
using Femto.Api;
using Femto.Api.Auth;
using Femto.Api.Infrastructure;
using Femto.Common;
using Femto.Common.Domain;
using Femto.Common.Integration;
using Femto.Modules.Auth.Application;
using Femto.Modules.Blog.Application;
using Femto.Modules.Media.Application;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.WebUtilities;

const string CorsPolicyName = "DefaultCorsPolicy";

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddOpenApi();

builder.Logging.ClearProviders();
builder.Logging.AddConsole();
builder.Logging.AddDebug();
builder.Logging.SetMinimumLevel(LogLevel.Information);


var connectionString = builder.Configuration.GetConnectionString("Database");
if (connectionString is null)
    throw new Exception("no database connection string found");

var blobStorageRoot = builder.Configuration.GetValue<string>("BlobStorageRoot");
if (blobStorageRoot is null)
    throw new Exception("no blob storage root found");


var eventBus = new EventBus(Channel.CreateUnbounded<IEvent>());
builder.Services.AddHostedService(_ => eventBus);

builder.Services.InitializeBlogModule(connectionString, eventBus);
builder.Services.InitializeMediaModule(connectionString, blobStorageRoot);
builder.Services.InitializeAuthenticationModule(connectionString, eventBus);

builder.Services.AddScoped<CurrentUserContext, CurrentUserContext>();
builder.Services.AddScoped<ICurrentUserContext>(s => s.GetRequiredService<CurrentUserContext>());


builder.Services.AddCors(options =>
{
    options.AddPolicy(
        CorsPolicyName,
        b =>
        {
            b.AllowAnyHeader()
                .AllowAnyMethod()
                .WithOrigins(builder.Configuration.GetValue<string>("CorsOrigins")?.Split(';') ?? [])
                .AllowCredentials();
        }
    );
});

builder
    .Services.AddControllers()
    .AddJsonOptions(options =>
    {
        options.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
        options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
        options.JsonSerializerOptions.DictionaryKeyPolicy = JsonNamingPolicy.CamelCase;
        options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
        options.JsonSerializerOptions.NumberHandling = JsonNumberHandling.AllowReadingFromString;
    });

builder.Services.Configure<CookieSettings>(
    builder.Configuration.GetSection("Cookies"));

builder
    .Services.AddAuthentication("SessionAuth")
    .AddScheme<AuthenticationSchemeOptions, SessionAuthenticationHandler>(
        "SessionAuth",
        options => { }
    );

builder.Services.AddAuthorization();

var app = builder.Build();

app.UseCors(CorsPolicyName);
app.UseAuthentication();
app.UseAuthorization();
app.UseExceptionHandler(errorApp =>
{
    errorApp.Run(async context =>
    {
        var exceptionHandlerFeature = context.Features.Get<IExceptionHandlerFeature>();
        var exception = exceptionHandlerFeature?.Error;
        var problemDetailsFactory =
            errorApp.ApplicationServices.GetRequiredService<ProblemDetailsFactory>();

        var statusCode = exception switch
        {
            DomainError => 400,
            _ => 500,
        };

        var message = exception switch
        {
            DomainError domainError => domainError.Message,
            { } e => e.Message,
            _ => ReasonPhrases.GetReasonPhrase(statusCode),
        };

        var problemDetails = problemDetailsFactory.CreateProblemDetails(
            httpContext: context,
            title: "An error occurred",
            detail: message,
            statusCode: statusCode
        );

        // problemDetails.Extensions["traceId"] = context.TraceIdentifier;
        context.Response.StatusCode = statusCode;
        context.Response.ContentType = "application/problem+json";

        await context.Response.WriteAsJsonAsync(problemDetails);
    });
});

// app.UseMiddleware<ExceptionMapperMiddleware>();
if (app.Environment.IsDevelopment())
{
    app.MapOpenApi();
}

app.MapControllers();

app.UseHttpsRedirection();

app.Run();
