using System;
using System.Threading.Tasks;
using Ablera.Serdica.Authentication.Extensions;
using Ablera.Serdica.UserConfiguration.Contracts;
using Ablera.Serdica.UserConfiguration.Models;
using Ablera.Serdica.Authority.Constants;
using Ablera.Serdica.Authority.Contracts;
using Ablera.Serdica.Authority.Extensions;
using Ablera.Serdica.Authority.Services;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using OpenIddict.Abstractions;
using OpenIddict.Server;
using OpenIddict.Server.AspNetCore;

namespace Ablera.Serdica.Authority.OpenIddictServerHandlers;

public sealed class AuthorizationRequestHandler(
    AuthenticationUrlBuilder authenticationUrlBuilder,
    IHttpContextAccessor httpContextAccessor,
    IUserManagingDirector<IdentityUser<string>> users,
    IUserConfigurationBuilder<UserConfigurationModel> userConfigurationBuilder,
    IUserConfigurationRepository<UserConfigurationModel> userConfigurationRepository,
    IOpenIddictApplicationManager manager) :
        IOpenIddictServerHandler<OpenIddictServerEvents.HandleAuthorizationRequestContext>
{
    public async ValueTask HandleAsync(
        OpenIddictServerEvents.HandleAuthorizationRequestContext ctx)
    {
        var request = httpContextAccessor.HttpContext?.GetOpenIddictServerRequest()
                      ?? throw new InvalidOperationException("No OIDC request found.");

        var result = await httpContextAccessor.HttpContext.AuthenticateAsync(
            CookieAuthenticationDefaults.AuthenticationScheme);

        // ------------ local session exists → issue code/token ------------
        if (result.Succeeded)
        {
            var userId = result.Principal.GetUserId();
            if (userId == null) return;
            var identityUser = await users.FindByIdAsync(userId);
            if (identityUser == null) return;

            var systemClaims = result.Principal.Claims ?? [];
            //var baseClaims = await users.GetBaseClaimsAsync(identityUser) ?? [];
            //var roleClaims = await users.GetRolesClaimsAsync(identityUser) ?? [];
            //HashSet<Claim> claims = [.. systemClaims, .. baseClaims, .. roleClaims];

            var principal = SerdicaPrincipalBuilder.Build(
                systemClaims,
                request.GetScopes(),
                OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);

            ctx.SignIn(principal);

            // store user configuration to be reused from microservices
            var userConfiguration = await userConfigurationBuilder.BuildUserConfigurationAsync(userId);
            await userConfigurationRepository.StoreAsync(userId, userConfiguration);

            return;
        }

        var client = await manager.FindByClientIdAsync(request.ClientId!);
        if (client is null) return;

        // ------------- no session → choose where to login -----------------
        var authenticationUrl = authenticationUrlBuilder.BuildAuthenticationUrl(
            request.ClientId!,
            (await manager.GetPropertiesAsync(client))
                .GetStringOrThrow(ConstantsClass.AuthenticationDelegateUrlKey, request.ClientId!),
            httpContextAccessor.HttpContext!.Request);
        if (authenticationUrl is null) return;

        httpContextAccessor.HttpContext!.Response.Redirect(authenticationUrl);
        ctx.HandleRequest();
    }
}
