using Microsoft.EntityFrameworkCore;
using NLog.Web;
using OpenIddict.Client;
using OpenIddict.Validation.AspNetCore;
using RobotNet.OpenIddictClient;
using RobotNet.ScriptManager.Data;
using RobotNet.ScriptManager.Services;
using static OpenIddict.Abstractions.OpenIddictConstants;

var builder = WebApplication.CreateBuilder(args);

//builder.Logging.ClearProviders();
builder.Host.UseNLog();

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection") 
                        ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");

var openIddictOption = builder.Configuration.GetSection(nameof(OpenIddictClientProviderOptions)).Get<OpenIddictClientProviderOptions>()
    ?? throw new InvalidOperationException("OpenID configuration not found or invalid format.");

builder.Services.AddDbContext<ScriptManagerDbContext>(options => options.UseSqlServer(connectionString));

builder.Services.AddControllers();
builder.Services.AddSignalR();

builder.Services.AddOpenIddict()
    .AddValidation(options =>
    {
        // Note: the validation handler uses OpenID Connect discovery
        // to retrieve the address of the introspection endpoint.
        options.SetIssuer(openIddictOption.Issuer);
        options.AddAudiences(openIddictOption.Audiences);

        // Configure the validation handler to use introspection and register the client
        // credentials used when communicating with the remote introspection endpoint.
        options.UseIntrospection()
               .SetClientId(openIddictOption.ClientId)
               .SetClientSecret(openIddictOption.ClientSecret);

        // Register the System.Net.Http integration.
        if (builder.Environment.IsDevelopment())
        {
            options.UseSystemNetHttp(httpOptions =>
            {
                httpOptions.ConfigureHttpClientHandler(context =>
                {
                    context.ServerCertificateCustomValidationCallback = (message, cert, chain, sslPolicyErrors) => true;
                });
            });
        }
        else
        {
            options.UseSystemNetHttp();
        }

        // Register the ASP.NET Core host.
        options.UseAspNetCore();
    })
    .AddClient(options =>
    {
        // Allow grant_type=client_credentials to be negotiated.
        options.AllowClientCredentialsFlow();

        // Disable token storage, which is not necessary for non-interactive flows like
        // grant_type=password, grant_type=client_credentials or grant_type=refresh_token.
        options.DisableTokenStorage();

        // Register the System.Net.Http integration and use the identity of the current
        // assembly as a more specific user agent, which can be useful when dealing with
        // providers that use the user agent as a way to throttle requests (e.g Reddit).
        options.UseSystemNetHttp()
               .SetProductInformation(typeof(Program).Assembly);

        var registration = new OpenIddictClientRegistration
        {
            Issuer = new Uri(openIddictOption.Issuer, UriKind.Absolute),
            GrantTypes = { GrantTypes.ClientCredentials },
            ClientId = openIddictOption.ClientId,
            ClientSecret = openIddictOption.ClientSecret,
        };

        foreach (var scope in openIddictOption.Scopes)
        {
            registration.Scopes.Add(scope);
        }

        // Add a client registration matching the client application definition in the server project.
        options.AddRegistration(registration);
    });

builder.Services.AddAuthentication(OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme);
builder.Services.AddAuthorization();

builder.Services.AddCors(options =>
{
    options.AddDefaultPolicy(policy =>
    {
        policy.AllowAnyOrigin()
            .AllowAnyMethod()
            .AllowAnyHeader()
            .WithExposedHeaders("Grpc-Status", "Grpc-Message", "Grpc-Encoding", "Grpc-Accept-Encoding");
    });
});

builder.Services.AddScoped<ScriptMissionCreator>();
builder.Services.AddSingleton<ScriptStateManager>();
builder.Services.AddSingleton<ScriptGlobalsManager>();
builder.Services.AddSingleton<ScriptMissionManager>();
builder.Services.AddSingleton<ScriptTaskManager>();
builder.Services.AddSingleton<ScriptConnectionManager>();
builder.Services.AddSingleton<DashboardPublisher>();
builder.Services.AddSingleton<DashboardConfig>();

builder.Services.AddHostedService(sp => sp.GetRequiredService<ScriptStateManager>());
builder.Services.AddHostedService(sp => sp.GetRequiredService<DashboardPublisher>());
builder.Services.AddHostedService(sp => sp.GetRequiredService<DashboardConfig>());

var app = builder.Build();
await app.Services.SeedScriptManagerDbAsync();
// Configure the HTTP request pipeline.

app.UseHttpsRedirection();

app.UseCors();

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

app.MapStaticAssets();
app.MapControllers();

app.MapHub<RobotNet.ScriptManager.Hubs.ConsoleHub>("/hubs/console"); 
app.MapHub<RobotNet.ScriptManager.Hubs.ProcessorHub>("/hubs/processor");
app.MapHub<RobotNet.ScriptManager.Hubs.ScriptTaskHub>("/hubs/scripttask");
app.MapHub<RobotNet.ScriptManager.Hubs.DashboardHub>("/hubs/dashboard");
app.MapHub<RobotNet.ScriptManager.Hubs.ScriptOpenHub>("/hubs/script-open");
app.MapHub<RobotNet.ScriptManager.Hubs.HMIHub>("/hubs/hmi");

app.Run();
