using Ablera.Serdica.Microservice.Consumer.Attributes;
using Ablera.Serdica.Microservice.Consumer.Contracts.Asynchronous;
using Ablera.Serdica.Common.Tools.Exceptions;
using Ablera.Serdica.Authority.Services;
using System.Threading.Tasks;
using Ablera.Serdica.Authentication.Constants;
using Ablera.Serdica.DBModels.Serdica;
using System.Linq;
using Ablera.Serdica.Authority.Models;
using Ablera.Serdica.Extensions.RabbitMQ.Contracts;
using Microsoft.EntityFrameworkCore;
using System;
using Ablera.Serdica.Microservice.Consumer.Contracts;
using static Ablera.Serdica.Authority.Constants.ConstantsClass;
using Ablera.Serdica.Authority.Contracts;
using Microsoft.AspNetCore.Identity;
using Ablera.Serdica.Authority.Constants;

namespace Ablera.Serdica.NotificationService.Endpoints;

public record UserChangePasswordRequest
{
    public required string? UserGuid { get; set; }
    public required string Password { get; init; }
    public required string ConfirmedPassword { get; init; }
}

public record UserChangePasswordResponse
{
    public required bool Succeeded { get; init; }
}

[Command("users_update_user_password", timeoutInSeconds: 10, methodName: "POST", allowedRoles: [SerdicaClaims.IsAuthenticated])]
public class UpdateUserPasswordEndpoint(
    IEndpointRequestMessageProvider requestMessageProvider,
    IUserManagingDirector<IdentityUser<string>> users,
    SerdicaDbContext dbContext)
        : Microservice.Consumer.Contracts.Asynchronous.IEndpointWithRequest<UserChangePasswordRequest, UserChangePasswordResponse>
{
    public async Task<UserChangePasswordResponse> ConsumeAsync(UserChangePasswordRequest request)
    {
        if (request.UserGuid != null && request.UserGuid != requestMessageProvider.RequestMessage.UserId)
        {
            var isLoggedInUserSuperUser = await dbContext.UserAccounts
                .Where(x => x.UserGuid == request.UserGuid)
                .SelectMany(x => x.UserRole1s)
                .Select(x => x.Id)
                .AnyAsync(x => x == SerdicaClaims.RoleSuperUser);
            if (request.UserGuid != requestMessageProvider.RequestMessage.UserId && isLoggedInUserSuperUser != true)
            {
                throw new BaseResultException("not_authorized".AsCode(), "You are not authorized to change configuration for this user!");
            }
        }
        request.UserGuid ??= requestMessageProvider.RequestMessage.UserId;

        var identityUser = await users.FindByIdAsync(request.UserGuid)
            ?? throw new BaseResultException("account_not_found".AsCode(), "Account associated with the session not found!");

        var result = await users.ChangePasswordAsync(identityUser, request.Password, request.ConfirmedPassword);
        if (result.Succeeded == false)
        {
            throw new BaseResultException(
                (result.ErrorCode ?? "change_password_failed").AsCode(), 
                MessageKeys.FailedToChangePassword);
        }

        return new UserChangePasswordResponse { Succeeded = true };
    }
}
