using System.Security.Claims;
using System.Text.Encodings.Web;
using Femto.Api.Sessions;
using Femto.Common;
using Femto.Modules.Auth.Application;
using Femto.Modules.Auth.Application.Interface.ValidateSession;
using Femto.Modules.Auth.Errors;
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Options;
using Microsoft.OpenApi.Extensions;

namespace Femto.Api.Auth;

internal class SessionAuthenticationHandler(
    IOptionsMonitor<AuthenticationSchemeOptions> options,
    ILoggerFactory logger,
    UrlEncoder encoder,
    IAuthModule authModule,
    CurrentUserContext currentUserContext,
    IOptions<CookieSettings> cookieOptions
) : AuthenticationHandler<AuthenticationSchemeOptions>(options, logger, encoder)
{
    protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        var sessionId = this.Request.Cookies["session"];
        if (string.IsNullOrWhiteSpace(sessionId))
            return AuthenticateResult.NoResult();

        try
        {
            var result = await authModule.Command(new ValidateSessionCommand(sessionId));

            var claims = new List<Claim>
            {
                new(ClaimTypes.Name, result.User.Username),
                new("sub", result.User.Id.ToString()),
                new("user_id", result.User.Id.ToString()),
            };

            claims.AddRange(
                result.User.Roles.Select(role => new Claim(ClaimTypes.Role, role.ToString()))
            );

            var identity = new ClaimsIdentity(claims, this.Scheme.Name);
            var principal = new ClaimsPrincipal(identity);

            this.Context.SetSession(result.Session, result.User, cookieOptions.Value);
            currentUserContext.CurrentUser = new CurrentUser(
                result.User.Id,
                result.User.Username,
                result.Session.SessionId
            );

            return AuthenticateResult.Success(
                new AuthenticationTicket(principal, this.Scheme.Name)
            );
        }
        catch (InvalidSessionError)
        {
            return AuthenticateResult.Fail("Invalid session");
        }
    }
}
