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.Common.Services.Contracts;
using Ablera.Serdica.UserConfiguration.Models;
using Ablera.Serdica.UserConfiguration.Contracts;
using Ablera.Serdica.Extensions.RabbitMQ.Contracts;

namespace Ablera.Serdica.NotificationService.Endpoints;

public class UpdateUsersConfigurationRequest
{
    public string? UserGuid { get; set; }
    public string? Language { get; set; }
    public string? Country { get; set; }
    public long? AutoLogoutMinutes { get; set; }
    public string? BranchCode { get; set; }
}

[Command("users_update_user_configuration", timeoutInSeconds: 10, allowedRoles: [SerdicaClaims.IsAuthenticated])]
public class UpdateUserConfigurationEndpoint(
    SerdicaDbContext dbContext,
    IEnvironment environment,
    IEndpointRequestMessageProvider requestMessageProvider,
    IUserConfigurationRepository<UserConfigurationModel> userConfigurationRepository,
    ILogger<UpdateUserConfigurationEndpoint> logger)
        : IEndpointWithRequest<UpdateUsersConfigurationRequest, UserConfigurationModel?>
{
    public async Task<UserConfigurationModel?> ConsumeAsync(UpdateUsersConfigurationRequest 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;

        logger.LogInformation("Attempting to update user profile for: '{userGuid}'.", request.UserGuid);
        using var tx = dbContext.Database.BeginTransaction();
        UserAccount? userAccount = null;
        try
        {
            userAccount = await dbContext.UserAccounts
                .Where(u => u.UserGuid == request.UserGuid)
                .FirstOrDefaultAsync();

            if (userAccount == null)
            {
                const string errorMsg =
                    "User identifier from a token does not match a user in the database: '{userGuid}'.";

                logger.LogError(errorMsg, request.UserGuid);
                throw new Exception(errorMsg.Replace("{userGuid}", request.UserGuid));
            }
            if (request.Language != null)
            {
                userAccount.Language = request.Language;
            }
            if (request.Country != null)
            {
                userAccount.Country = request.Country;
            }
            if (request.AutoLogoutMinutes.HasValue)
            {
                userAccount.AutoLogoutMinutes = request.AutoLogoutMinutes switch
                {
                    null => environment.DefaultAutoLogoutInMinutes, // Use default when not provided
                    var minutes when minutes > environment.MaximumAutoLogoutInMinutes => throw new BaseResultException(
                        $"The auto logout minutes value cannot be greater than the maximum allowed: {environment.MaximumAutoLogoutInMinutes}"),
                    var minutes when minutes < environment.MinimumAutoLogoutInMinutes => throw new BaseResultException(
                        $"The auto logout minutes value cannot be less than the minimum allowed: {environment.MinimumAutoLogoutInMinutes}"),
                    var minutes => minutes // Otherwise, use the provided value
                };
            }
            if (request.BranchCode != null)
            {
                await dbContext.UserAccounts
                        .Where(ua => ua.UserAccountId == userAccount.UserAccountId)
                        .ExecuteUpdateAsync(ua =>
                            ua.SetProperty(
                                prop => prop.CurrentBranch,
                                dbContext.IcUsers
                                    .Include(icUser => icUser.IcBranch)
                                    .Where(icUser => icUser.UserAccountId == userAccount.UserAccountId
                                        && icUser.IcBranch.BranchCode == request.BranchCode)
                                    .Select(icUserId => icUserId.IcUserId)
                                    .FirstOrDefault()
                            )
                        );
            }

            await dbContext.SaveChangesAsync();
            await tx.CommitAsync();
            logger.LogInformation("Successfully updated settings for user: '{username}'.", userAccount.UserName);
        }
        catch (Exception ex)
        {
            await tx.RollbackAsync();
            logger.LogError(
                ex,
                "Failed to update user profile for: '{userGuid}'.",
                request.UserGuid);
            throw;
        }

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

        return userConfiguration;
    }
}

