using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using OpenIddict.Abstractions;
using static OpenIddict.Abstractions.OpenIddictConstants;

namespace RobotNet.IdentityServer.Data;

public static class ApplicationDbExtensions
{
    public static async Task SeedApplicationDbAsync(this IServiceProvider serviceProvider)
    {
        using var scope = serviceProvider.GetRequiredService<IServiceScopeFactory>().CreateScope();

        using var appDb = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();

        await appDb.Database.MigrateAsync();
        //await appDb.Database.EnsureCreatedAsync();
        await appDb.SaveChangesAsync();

        await scope.ServiceProvider.SeedRolesAsync();
        await scope.ServiceProvider.SeedUsersAsync();
        await scope.ServiceProvider.SeedOpenIddictApplicationAsync();
        await scope.ServiceProvider.SeedOpenIddictScopesAsync();
    }

    private static async Task SeedRolesAsync(this IServiceProvider serviceProvider)
    {
        var roleManager = serviceProvider.GetRequiredService<RoleManager<ApplicationRole>>();
        if (!await roleManager.RoleExistsAsync("Administrator"))
        {
            await roleManager.CreateAsync(new ApplicationRole()
            {
                Name = "Administrator",
                NormalizedName = "ADMINISTRATOR",
                CreatedDate = DateTime.UtcNow
            });
        }
    }

    private static async Task SeedUsersAsync(this IServiceProvider serviceProvider)
    {
        using var userManager = serviceProvider.GetRequiredService<UserManager<ApplicationUser>>();
        if (await userManager.FindByNameAsync("admin") is null)
        {
            var admin = new ApplicationUser()
            {
                UserName = "admin",
                Email = "administrator@phenikaa-x.com",
                NormalizedUserName = "ADMINISTRATOR",
                NormalizedEmail = "ADMINISTRATOR@PHENIKAA-X.COM",
                EmailConfirmed = true,
            };

            await userManager.CreateAsync(admin, "robotics");
            await userManager.AddToRoleAsync(admin, "Administrator");
        }
    }

    private static async Task CreateIfNotExistAsync(this IOpenIddictApplicationManager manager, OpenIddictApplicationDescriptor desciptor)
    {
        if (desciptor.ClientId == null) return;
        if (await manager.FindByClientIdAsync(desciptor.ClientId) == null)
        {
            await manager.CreateAsync(desciptor);
        }
    }

    private static async Task SeedOpenIddictApplicationAsync(this IServiceProvider serviceProvider)
    {
        var manager = serviceProvider.GetRequiredService<IOpenIddictApplicationManager>();

        await manager.CreateIfNotExistAsync(new OpenIddictApplicationDescriptor
        {
            ClientId = "robotnet-webapp",
            ConsentType = ConsentTypes.Explicit,
            DisplayName = "RobotNet WebApp",
            ClientType = ClientTypes.Public,
            PostLogoutRedirectUris =
                {
                    new Uri("https://localhost:7035/authentication/logout-callback")
                },
            RedirectUris =
                {
                    new Uri("https://localhost:7035/authentication/login-callback")
                },
            Permissions =
                {
                    Permissions.Endpoints.Authorization,
                    Permissions.Endpoints.EndSession,
                    Permissions.Endpoints.Token,
                    Permissions.GrantTypes.AuthorizationCode,
                    Permissions.GrantTypes.RefreshToken,
                    Permissions.ResponseTypes.Code,
                    Permissions.Scopes.Email,
                    Permissions.Scopes.Profile,
                    Permissions.Scopes.Roles,
                    Permissions.Prefixes.Scope + "robotnet-script-api",
                    Permissions.Prefixes.Scope + "robotnet-robot-api",
                    Permissions.Prefixes.Scope + "robotnet-map-api",
                },
            Requirements =
                {
                    Requirements.Features.ProofKeyForCodeExchange,
                },
        });


        await manager.CreateIfNotExistAsync(new OpenIddictApplicationDescriptor
        {
            ClientId = "robotnet-script-manager",
            ClientSecret = "05594ECB-BBAE-4246-8EED-4F0841C3B475",
            Permissions =
                {
                    Permissions.Endpoints.Introspection,
                    Permissions.GrantTypes.ClientCredentials,
                    Permissions.Endpoints.Token,
                    Permissions.Prefixes.Scope + "robotnet-robot-api",
                    Permissions.Prefixes.Scope + "robotnet-map-api",
                }
        });

        await manager.CreateIfNotExistAsync(new OpenIddictApplicationDescriptor
        {
            ClientId = "robotnet-map-manager",
            ClientSecret = "72B36E68-2F2B-455B-858A-77B1DCC79979",
            Permissions =
                {
                    Permissions.Endpoints.Introspection,
                }
        });

        await manager.CreateIfNotExistAsync(new OpenIddictApplicationDescriptor
        {
            ClientId = "robotnet-robot-manager",
            ClientSecret = "469B2DEB-660E-4C91-97C7-D69550D9969D",
            Permissions =
                {
                    Permissions.Endpoints.Introspection,
                    Permissions.GrantTypes.ClientCredentials,
                    Permissions.Endpoints.Token,
                    Permissions.Prefixes.Scope + "robotnet-map-api",
                }
        });
    }

    private static async Task CreateIfNotExistAsync(this IOpenIddictScopeManager manager, OpenIddictScopeDescriptor desciptor)
    {
        if (desciptor.Name == null) return;
        if (await manager.FindByNameAsync(desciptor.Name) is null)
        {
            await manager.CreateAsync(desciptor);
        }
    }

    private static async Task SeedOpenIddictScopesAsync(this IServiceProvider serviceProvider)
    {
        var manager = serviceProvider.GetRequiredService<IOpenIddictScopeManager>();

        await manager.CreateIfNotExistAsync(new OpenIddictScopeDescriptor
        {
            DisplayName = "RobotNet Script Manager API Access",
            Name = "robotnet-script-api",
            Resources =
                {
                    "robotnet-script-manager"
                }
        });

        await manager.CreateIfNotExistAsync(new OpenIddictScopeDescriptor
        {
            DisplayName = "RobotNet Map Manager API Access",
            Name = "robotnet-map-api",
            Resources =
                {
                    "robotnet-map-manager"
                }
        });

        await manager.CreateIfNotExistAsync(new OpenIddictScopeDescriptor
        {
            DisplayName = "RobotNet Robot Manager API Access",
            Name = "robotnet-robot-api",
            Resources =
                {
                    "robotnet-robot-manager"
                }
        });
    }

}
