using Lactose.Authorization;
using Lactose.Configuration;
using Lactose.Context;
using Lactose.Models;
using Lactose.Repositories;
using Lactose.Services;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using Npgsql;

WebApplicationBuilder builder = WebApplication.CreateBuilder(args);

//Adds configurations to the builder
builder.Configuration.AddJsonFile("appsettings.json", true, true)
    .AddJsonFile($"appsettings.{builder.Environment.EnvironmentName}.json", true, true);

builder.Services.Configure<RouteOptions>(options => options.LowercaseUrls = true);

builder.Services.ConfigureOptions<SignKeyProvider>();

string? user     = builder.Configuration["DatabaseCredentials:UserID"];
string? password = builder.Configuration["DatabaseCredentials:Password"];
string  database = builder.Configuration["DatabaseCredentials:Database"] ?? "lactose";
string  host     = builder.Configuration["DatabaseAddress:Host"] ?? "localhost";
string  port     = builder.Configuration["DatabaseAddress:Port"] ?? "3306";

var strBuilder = new NpgsqlConnectionStringBuilder() {
    Host     = host,
    Port     = int.Parse(port),
    Username = user,
    Password = password,
    Database = database
};

builder.Services.AddDbContext<LactoseDbContext>(
    options => {
        /*
        options.UseMySql(dbConnString, new MariaDbServerVersion(new Version(11, 5, 2)))
           #if DEBUG
               .LogTo(Console.WriteLine)
               .EnableSensitiveDataLogging()
               .EnableDetailedErrors()
        #endif
            ;*/

        options.UseNpgsql(strBuilder.ConnectionString)
            #if DEBUG
                .LogTo(Console.WriteLine)
                .EnableSensitiveDataLogging()
                .EnableDetailedErrors()
            #endif
            //.ConfigureWarnings(warnings => warnings.Ignore(RelationalEventId.PendingModelChangesWarning))
            ;
    }
);

AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);

// Add services to initialize an empty the database
builder.Services.AddScoped<IDbInitializer, DbInitializer>();    


// Add services to the container.
builder.Services.AddTransient<IPasswordHasher<User>, PasswordHasher<User>>();
builder.Services.AddTransient<ISettingsRepository, SettingsRepository>();
builder.Services.AddTransient<IFolderRepository, FolderRepository>();
builder.Services.AddTransient<IUserRepository,UserRepository>();
builder.Services.AddTransient<ITagRepository, TagRepository>();
builder.Services.AddTransient<IAssetRepository, AssetRepository>();
builder.Services.AddTransient<IAlbumRepository, AlbumRepository>();
builder.Services.AddTransient<IMediaRepository, MediaRepository>();
builder.Services.AddTransient<ITokenService, TokenService>();
builder.Services.AddTransient<LactoseAuthService>();
builder.Services.AddTransient<FileSystemScannerService>();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddControllers();

builder.Services.AddSwaggerGen(
    options => {
        options.SwaggerDoc(
            "v1",
            new OpenApiInfo {
                Title   = "Lactose",
                Version = "v1"
            }
        );
        options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme {
            In           = ParameterLocation.Header, 
            Description  = "Enter the Bearer Authorization string as following: `Bearer <Generated-JWT-Token>`",
            Name         = "Authorization",
            BearerFormat = "JWT",
            Scheme       = "bearer",
            Type         = SecuritySchemeType.ApiKey
        });
        options.AddSecurityRequirement(new OpenApiSecurityRequirement {
            { 
                new OpenApiSecurityScheme { 
                    Reference = new OpenApiReference { 
                        Type = ReferenceType.SecurityScheme,
                        Id   = "Bearer",
                    },
                    
                },
                []
            } 
        });

        options.SupportNonNullableReferenceTypes();
        options.DescribeAllParametersInCamelCase();
        options.UseInlineDefinitionsForEnums();
    }
);
SecurityKey securityKey = new SignKeyProvider(builder.Configuration).Get().GetSecurityKey();

builder.Services.AddTransient<IClaimsTransformation, RefreshTokenTransformation>();

var policy = new AuthorizationPolicyBuilder()
             .AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
             .RequireAuthenticatedUser()
             .RequireClaim(RefreshTokenTransformation.ClaimType)
             .Build();

builder.Services.AddAuthorization(options => 
{
    AuthorizationPolicy defaultPolicy = 
        new AuthorizationPolicyBuilder(JwtBearerDefaults.AuthenticationScheme)
            .RequireAuthenticatedUser()
            .RequireClaim(RefreshTokenTransformation.ClaimType,"true")
            .Build();
});

builder.Services
       .AddAuthentication(x =>
       {
           x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
           x.DefaultChallengeScheme    = JwtBearerDefaults.AuthenticationScheme;
       })
       .AddJwtBearer(x =>
       {
           x.RequireHttpsMetadata = false;
           x.SaveToken            = true;
           x.TokenValidationParameters = new TokenValidationParameters
           {
               IssuerSigningKey = securityKey,
               ValidateIssuer   = false,
               ValidateAudience = false
           };
       });

//Add the background scan service
//builder.Services.AddHostedService<FileSystemScannerService>();

WebApplication app = builder.Build();

using (var scope = app.Services.CreateScope()) {
    var dbInitializer = scope.ServiceProvider.GetRequiredService<IDbInitializer>();
    dbInitializer.Initialize();
}

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment()) {
    app.UseSwagger();
    app.UseSwaggerUI(c =>
    {
        c.SwaggerEndpoint("/swagger/v1/swagger.json", "AuthCore API V1");
    });
}
app.UseAuthentication();
app.UseAuthorization();
//app.UseHttpsRedirection();
app.MapControllers();
app.Run();
