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.Authentication.Models;
using System;
using System.ComponentModel.DataAnnotations;
using Ablera.Serdica.DBModels.Serdica;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Ablera.Serdica.UserConfiguration.Models;
using Ablera.Serdica.Common.Services.Contracts;
using Ablera.Serdica.UserConfiguration.Contracts;
using Ablera.Serdica.Extensions.RabbitMQ.Contracts;

namespace Ablera.Serdica.NotificationService.Endpoints;

public record UpdateUserRoles
{
    public required string UserGuid { get; set; }

    public required string[] Roles { get; init; }
}

[Command("users_update_user_roles", timeoutInSeconds: 10, allowedRoles: [SerdicaClaims.RoleSuperUser])]
public class UpdateUserRolesEndpoint(
    SerdicaDbContext dbContext,
    ILogger<UpdateUserConfigurationEndpoint> logger,
    IUserConfigurationRepository<UserConfigurationModel> userConfigurationRepository)
        : IEndpointWithRequest<UpdateUserRoles, UserConfigurationModel?>
{
    public async Task<UserConfigurationModel?> ConsumeAsync(UpdateUserRoles request)
    {
        using var tx = dbContext.Database.BeginTransaction();
        try
        {
            // we are using raw sql because the entityframework generator fails to create entity of table contained only by two columns that are FKs
            var userAccountId = await dbContext.UserAccounts
                .Where(u => u.UserGuid == request.UserGuid)
                .Select(u => u.UserAccountId)
                .FirstOrDefaultAsync();

            // delete old roles
            await dbContext.Database.ExecuteSqlRawAsync(
                "DELETE FROM SRD_SYS.USER_ROLES WHERE USER_ACCOUNT_ID = {0} ", userAccountId);

            // insert new roles
            foreach (var role in request.Roles)
            {
                await dbContext.Database.ExecuteSqlRawAsync(
                    "INSERT INTO SRD_SYS.USER_ROLES (USER_ACCOUNT_ID, USER_ROLE) ({0}, {1})", userAccountId, role);
            }

            await dbContext.SaveChangesAsync();

            await tx.CommitAsync();
        }
        catch (Exception ex)
        {
            await tx.RollbackAsync();
            logger.LogError(
                ex,
                "Failed to update user roles for: '{userGuid}'.",
                request.UserGuid);
            throw;
        }

        logger.LogInformation("Successfully update roles for user with identifier: '{identifier}'.", request.UserGuid);

        // Rebuild the configuration to include the updates roles
        var userConfiguration = await userConfigurationRepository.RetrieveAsync(request.UserGuid, true);

        return userConfiguration;
    }
}

