using BlazorComponentBus;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using MudBlazor.Services;
using NLog.Web;
using Quartz;
using RobotNet.IdentityServer.Components;
using RobotNet.IdentityServer.Components.Account;
using RobotNet.IdentityServer.Components.Layout;
using RobotNet.IdentityServer.Data;
using RobotNet.IdentityServer.Services;
using System.Security.Cryptography.X509Certificates;
using static OpenIddict.Abstractions.OpenIddictConstants;

var builder = WebApplication.CreateBuilder(args);

builder.Host.UseNLog();
// builder.AddServiceDefaults();

builder.Services.AddControllers();
builder.Services.AddControllersWithViews();
builder.Services.AddMudServices();
// Add services to the container.
builder.Services.AddRazorComponents()
    .AddInteractiveServerComponents();

builder.Services.AddScoped<ComponentBus>();
builder.Services.AddCascadingAuthenticationState();
builder.Services.AddScoped<NavMenu>();
builder.Services.AddSingleton<PasswordStrengthService>();
builder.Services.AddSingleton<UserInfoService>();
builder.Services.AddScoped<UserImageService>();
builder.Services.AddScoped<IdentityService>();
builder.Services.AddScoped<IdentityUserAccessor>();
builder.Services.AddScoped<IdentityRedirectManager>();
builder.Services.AddScoped<AuthenticationStateProvider, IdentityRevalidatingAuthenticationStateProvider>();

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection") ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
{
    options.UseSqlServer(connectionString);
    options.UseOpenIddict();
});
builder.Services.AddDatabaseDeveloperPageExceptionFilter();

builder.Services.AddIdentity<ApplicationUser, ApplicationRole>(options =>
    {
        options.SignIn.RequireConfirmedAccount = true;
        options.Lockout.AllowedForNewUsers = false;
        options.Password.RequireNonAlphanumeric = false;
        options.Password.RequireUppercase = false;
        options.Password.RequireLowercase = false;
        options.Password.RequireDigit = false;
    })
    .AddEntityFrameworkStores<ApplicationDbContext>()
    .AddDefaultTokenProviders();

builder.Services.AddSingleton<IEmailSender<ApplicationUser>, IdentityNoOpEmailSender>();


builder.Services.AddQuartz(options =>
{
    options.UseSimpleTypeLoader();
    options.UseInMemoryStore();
});

builder.Services.AddQuartzHostedService(options => options.WaitForJobsToComplete = true);

builder.Services.AddOpenIddict()
    .AddCore(options =>
    {
        // Configure OpenIddict to use the Entity Framework Core stores and models.
        // Note: call ReplaceDefaultEntities() to replace the default OpenIddict entities.
        options.UseEntityFrameworkCore()
                .UseDbContext<ApplicationDbContext>();

        // Enable Quartz.NET integration.
        options.UseQuartz();
    })
    .AddServer(options =>
    {
        options.SetIssuer(builder.Configuration["OpenIddictCertificate:Issuer"] ?? throw new InvalidOperationException("OpenIddictCertificate Issuer is not configured."));

        // Enable the authorization, logout, token and userinfo endpoints.
        options.SetAuthorizationEndpointUris("api/Authorization/connect/authorize")
                .SetEndSessionEndpointUris("api/Authorization/connect/logout")
                .SetIntrospectionEndpointUris("connect/introspect")
                .SetTokenEndpointUris("api/Authorization/connect/token")
                .AllowClientCredentialsFlow()
                .SetUserInfoEndpointUris("api/Userinfo")
                .SetEndUserVerificationEndpointUris("connect/verify");

        // Mark the "email", "profile" and "roles" scopes as supported scopes.
        options.RegisterScopes(Scopes.Email, Scopes.Profile, Scopes.Roles);

        // Note: this sample only uses the authorization code and refresh token
        // flows but you can enable the other flows if you need to support
        // implicit, password or client credentials.
        options.AllowAuthorizationCodeFlow()
                .AllowRefreshTokenFlow()
                .AllowClientCredentialsFlow();

        if (builder.Environment.IsDevelopment())
        {
            // Register the signing and encryption credentials.
            options.AddDevelopmentEncryptionCertificate()
                    .AddDevelopmentSigningCertificate();

            // Thêm ephemeral encryption key
            //options.AddEphemeralEncryptionKey()
            //       .AddEphemeralSigningKey(); // Thêm signing key tạm thời
        }
        else if (builder.Environment.IsProduction())
        {
            // Thêm ephemeral encryption key
            // Sử dụng chứng chỉ thực tế
            var path = builder.Configuration["OpenIddictCertificate:Path"] ?? throw new InvalidOperationException("Certificate path is not configured.");
            var password = builder.Configuration["OpenIddictCertificate:Password"] ?? throw new InvalidOperationException("Certificate password is not configured.");
            if (string.IsNullOrEmpty(path) || string.IsNullOrEmpty(password))
            {
                throw new InvalidOperationException("Certificate path or password is not configured.");
            }

            var certificate = X509CertificateLoader.LoadPkcs12FromFile(path, password);
            options.AddEncryptionCertificate(certificate)
                   .AddSigningCertificate(certificate);
        }

        options.UseDataProtection()
               .PreferDefaultAccessTokenFormat()
               .PreferDefaultAuthorizationCodeFormat()
               .PreferDefaultRefreshTokenFormat();
        // Register the ASP.NET Core host and configure the ASP.NET Core-specific options.
        options.UseAspNetCore()
                .EnableAuthorizationEndpointPassthrough()
                .EnableEndSessionEndpointPassthrough()
                .EnableTokenEndpointPassthrough()
                .EnableUserInfoEndpointPassthrough()
                .EnableStatusCodePagesIntegration();
        // Can thiệp vào sự kiện logging
    })
    .AddValidation(options =>
    {
        // Import the configuration from the local OpenIddict server instance.
        options.UseLocalServer();

        // Register the ASP.NET Core host.
        options.UseAspNetCore();
    });

builder.Services.AddCors(options =>
{
    options.AddPolicy("RequestAuthorize", policy =>
    {
        policy.AllowAnyOrigin()
            .AllowAnyMethod()
            .AllowAnyHeader();
    });
});

builder.Services.AddMudServices();

var app = builder.Build();

await app.Services.SeedApplicationDbAsync();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseMigrationsEndPoint();
}
else
{
    app.UseExceptionHandler("/Error", createScopeForErrors: true);
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCors("RequestAuthorize");

app.MapControllers();
app.UseAuthentication();
app.UseAuthorization();

app.UseAntiforgery();

app.MapStaticAssets();
app.MapRazorComponents<App>()
    .AddInteractiveServerRenderMode();

app.MapAdditionalIdentityEndpoints();

app.Run();
