using adas_core.Application.Repositories.Interfaces;
using adas_core.Application.Services.Interfaces;
using adas_core.Authentication.Interfaces;
using adas_core.Domain.Models.MongoModels;
using Microsoft.AspNetCore.Http;
using MongoDB.Bson;
using Serilog;

namespace adas_core.Authentication;

public class AuthorityService(
    IAuthorityRepository authorityRepository,
    IHttpContextAccessor httpContextAccessor,
    ILocalAuditService auditService)
    : IAuthorityService
{
    /// <summary>
    /// Creates a new authority for the specified user with the given role name by delegating to the authority repository.
    /// </summary>
    /// <param name="roleName">The name of the role to associate with the authority.</param>
    /// <param name="userId">The unique identifier of the user for whom the authority is being created.</param>
    public void CreateNew(string roleName, ObjectId userId)
        {
            authorityRepository.CreateNewAuthority(roleName, userId);
        }

    /// <summary>
    /// Inserts a new <paramref name="authorization"/> into the authority repository and records an audit log entry for the operation using the current HTTP context user.
    /// </summary>
    /// <param name="authorization">The authorization entity to be inserted and tracked in the audit log.</param>
    public async Task InsertOne(Authorization authorization)
        {
            await authorityRepository.InsertOneAsync(authorization);
            await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, null, authorization);
        }

    /// <summary>
    /// Updates an existing authorization record in the repository and records an audit log entry capturing the previous and updated states for the current user.
    /// </summary>
    /// <param name="authorization">The authorization entity containing the identifier of the record to update and its new values.</param>
    public async Task updateOne(Authorization authorization)
        {
            var oldAuth = await authorityRepository.GetById(authorization.Id);
            await authorityRepository.UpdateOneAsync(authorization.Id, authorization);
            await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, oldAuth, authorization);
        }

    /// <summary>
    ///     Retrieves the authorities of the specified user.
    /// </summary>
    /// <param name="userId"></param>
    /// <returns>A list of the user's authorities.</returns>
    public Task<List<Authorization>> GetUserAuthorities(ObjectId userId)
    {
        return authorityRepository.GetUserAuthorities(userId);
    }

    /// <summary>
    /// Retrieves all available authorities from the underlying repository.
    /// </summary>
    /// <returns>A task that represents the asynchronous operation, containing a list of all <see cref="Authorization"/> records.</returns>
    public Task<List<Authorization>> GetAllAuthorities()
        {
            return authorityRepository.GetAllAuthorities();
        }

    /// <summary>
    /// Deletes all authorities associated with the specified user and records an audit log entry for each removed authority. Returns <c>false</c> if no authorities were deleted or if an exception is encountered during processing.
    /// </summary>
    /// <param name="id">The unique identifier of the user whose authorities should be deleted.</param>
    /// <returns><c>true</c> if the authorities were successfully deleted; otherwise, <c>false</c>.</returns>
    public async Task<bool> DeleteAuthoritiesForUser(ObjectId id)
        {
            try
            {
                var list = await authorityRepository.GetUserAuthorities(id);
                var result = await authorityRepository.DeleteAllAuthoritiesByUser(id);
                if (!result) return result;
    
                foreach (var auth in list)
                    await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, auth, null);
                return result;
            }
            catch (Exception e)
            {
                Log.Error("Exception deleting authorities for user {id}. Exception: {e}", id, e.Message);
                return false;
            }
        }

    /// <summary>
    /// Creates a new user authority by persisting the provided authorization record and writing an audit log entry for the operation.
    /// </summary>
    /// <param name="authorization">The authorization entity to be created and stored.</param>
    /// <returns>The created <see cref="Authorization"/> entity.</returns>
    public async Task<Authorization?> CreateNewUserAuthority(Authorization authorization)
        {
            await authorityRepository.InsertOneAsync(authorization);
            await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, null, authorization);
            return authorization;
        }

    /// <summary>
    /// Deletes a user authority by its parsed identifier and records an audit log entry when the deletion succeeds.
    /// Returns <c>true</c> if the authority was found and removed, and <c>false</c> when the entity is not found or an exception occurs during processing.
    /// </summary>
    /// <param name="userAuthorityIdParsed">The parsed identifier of the user authority to delete.</param>
    /// <returns><c>true</c> if the user authority was successfully deleted; otherwise, <c>false</c>.</returns>
    public async Task<bool> DeleteUserAuthority(ObjectId userAuthorityIdParsed)
        {
            try
            {
                var result = await authorityRepository.DeleteAsync(userAuthorityIdParsed);
                if (result != null)
                    await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, result, null);
                return result != null;
            }
            catch (Exception e)
            {
                Log.Error("Exception deleting user authority {userAuthorityIdParsed}. Exception: {e}",
                    userAuthorityIdParsed, e.Message);
                return false;
            }
        }

    /// <summary>
    /// Edits an existing user authority, records the change in an audit log, and returns the updated authorization.
    /// Returns <c>null</c> if the update operation fails due to an exception.
    /// </summary>
    /// <param name="authorization">The authorization entity containing the updated information to persist.</param>
    /// <returns>The updated <see cref="Authorization"/> on success, or <c>null</c> if an error occurs during the operation.</returns>
    public async Task<Authorization?> EditUserAuthority(Authorization authorization)
        {
            try
            {
                var oldAuth = await authorityRepository.GetById(authorization.Id);
                await authorityRepository.UpdateOneAsync(authorization.Id, authorization);
                await auditService.CreateAuditLogAsync(httpContextAccessor.HttpContext?.User!, oldAuth, authorization);
                return authorization;
            }
            catch (Exception e)
            {
                Log.Error("Exception editing user authority {authorization}. Exception: {e}", authorization, e.Message);
                return null;
            }
        }
}