using Microsoft.EntityFrameworkCore;
using NLog.Web;
using OpenIddict.Client;
using OpenIddict.Validation.AspNetCore;
using RobotNet.OpenIddictClient;
using RobotNet.RobotManager.Data;
using RobotNet.RobotManager.HubClients;
using RobotNet.RobotManager.Hubs;
using RobotNet.RobotManager.Services;
using RobotNet.RobotManager.Services.OpenACS;
using RobotNet.RobotManager.Services.Traffic;
using System.Net.Http.Headers;
using static OpenIddict.Abstractions.OpenIddictConstants;

var builder = WebApplication.CreateBuilder(args);
builder.Host.UseNLog();

//builder.AddServiceDefaults();

// Add services to the container.

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

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

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();
    });
});

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

//builder.Services.AddHttpContextAccessor();
builder.Services.AddSingleton<MapManagerAccessTokenHandler>();
builder.Services.AddHttpClient("MapManagerAPI", client =>
{
    client.BaseAddress = new Uri(mapManagerOptions.Url);
    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}).AddHttpMessageHandler<MapManagerAccessTokenHandler>();

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection") ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
Action<DbContextOptionsBuilder> appDbOptions = options => options.UseSqlServer(connectionString, b => b.MigrationsAssembly("RobotNet.RobotManager"));
builder.Services.AddDbContext<RobotEditorDbContext>(appDbOptions);

builder.Services.AddTransient<MapHubClient>();
builder.Services.AddSingleton<RobotEditorStorageRepository>();
builder.Services.AddSingleton(typeof(LoggerController<>));
builder.Services.AddSingleton<TrafficManager>();
builder.Services.AddHostedService<MqttBroker>();
builder.Services.AddSingleton<PathPlanner>();
builder.Services.AddSingleton<RobotManager>();
builder.Services.AddSingleton<MapManager>();
builder.Services.AddSingleton<RobotPublisher>();
builder.Services.AddSingleton<TrafficPublisher>();
builder.Services.AddHostedService(sp => sp.GetRequiredService<TrafficPublisher>());
builder.Services.AddHostedService(sp => sp.GetRequiredService<RobotPublisher>());
builder.Services.AddHostedService(sp => sp.GetRequiredService<RobotManager>());
builder.Services.AddHostedService(sp => sp.GetRequiredService<MapManager>());
builder.Services.AddHostedService(sp => sp.GetRequiredService<TrafficManager>());
builder.Services.AddSingleton<TrafficACS>();
builder.Services.AddSingleton<OpenACSManager>();
builder.Services.AddSingleton<OpenACSPublisher>();
builder.Services.AddHostedService(sp => sp.GetRequiredService<OpenACSManager>());
builder.Services.AddHostedService(sp => sp.GetRequiredService<OpenACSPublisher>());

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

app.UseHttpsRedirection();

app.UseCors();

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

app.MapControllers();

app.MapHub<RobotHub>("/hubs/robot/online");
app.MapHub<TrafficHub>("/hubs/traffic");
app.MapHub<RobotManagerHub>("/hubs/robot-manager");

app.Run();
