using IWS5.Core.DTO;
using IWS5.Core.Enums;
using IWS5.Core.Exceptions;
using IWS5.Core.Models;
using IWS5.Core.Models.Request;
using IWS5.Core.Models.Request.Process;
using IWS5.Core.Services;
using IWS5.DatabaseAccess.Interfaces;
using Serilog;

namespace IWS5.Services;

public class RightsService(
    IRightsRepository rightsRepository,
    IButtonRepository buttonRepository,
    IQueueRepository queueRepository,
    IFieldDefinitionRepository fieldDefinitionRepository,
    IDocumentCabinetRepository documentCabinetRepository,
    IProcessRepository processRepository,
    IUserRepository userRepository,
    ILogger logger) : IRightsService
{
    public async Task UpdateRights(UpdateRightRequest request)
    {
        logger.Information("Starting updating permissions by User {UserName}", request.UserName);

        var updatedRightsCount = await rightsRepository.UpdateRight(request.UpdateRequestItem.ToList());

        logger.Information("{count} rights updated by user {userName}", updatedRightsCount, request.UserName);
    }

    public async Task<Response<ProcessPermissionsDTO>> ListProcessRights(ListProcessRightsRequest request)
    {
        logger.Information(
            "Starting to list process rights for ProcessId {ProcessId} and GroupId {GroupId} by User {UserName}",
            request.ProcessId, request.GroupId, request.UserName);

        logger.Information("Retrieving rights for GroupId {GroupId}", request.GroupId);
        var rights = await rightsRepository.ListAsync(right => right.UserGuid == request.GroupId);

        logger.Information("Retrieving process details for ProcessId {ProcessId}", request.ProcessId);
        var process = await processRepository.FindAsync(process => process.ProcessId == request.ProcessId);

        logger.Information("Retrieving folder fields for ProcessId {ProcessId}", request.ProcessId);
        var folderFields = await fieldDefinitionRepository.ListAsync(field => field.ProcessId == request.ProcessId);

        logger.Information("Retrieving all document templates");
        var templates = await documentCabinetRepository.ListAsync();

        logger.Information("Retrieving default document template for ProcessId {ProcessId}", request.ProcessId);
        var defaultTemplate = await documentCabinetRepository.GetProcessDefaultDocumentTemplate(request.ProcessId);

        logger.Information("Processing rights specific logic");
        var processRights = rights
            .Where(right => right.ObjectType == (int)ObjectType.Process && right.ObjectGuid == process.ObjectId)
            .Select(right => (ATRightAllowRight)right.AllowRights).ToList().AsReadOnly();

        logger.Information("Processing folder field rights specific logic");
        var folderFieldRights = folderFields
            .Select(field => new ProcessFieldPermissionsDTO(
                field.FieldId,
                field.ObjectId,
                field.FieldName,
                rights.Any(right =>
                    right.ObjectType == (int)ObjectType.FolderField && right.ObjectGuid == field.ObjectId &&
                    right.AllowRights == (int)ATRightAllowRight.ViewFolderField),
                rights.Any(right =>
                    right.ObjectType == (int)ObjectType.FolderField && right.ObjectGuid == field.ObjectId &&
                    right.AllowRights == (int)ATRightAllowRight.EditFolderField)))
            .ToList().AsReadOnly();

        logger.Information("Processing allowed templates specific logic");
        var allowedTemplates = templates
            .Select(template => new DocumentTemplateListItemDTO(
                template.ObjectId,
                template.CabinetName,
                defaultTemplate.ObjectId == template.ObjectId))
            .ToList().AsReadOnly();

        logger.Information("Processing document field rights specific logic");
        var documentFieldRights = defaultTemplate.AtdocumentCabinetDefs
            .Select(cabinetDef => new ProcessFieldPermissionsDTO(
                cabinetDef.FieldId,
                cabinetDef.ObjectId,
                cabinetDef.FieldName,
                rights.Any(right =>
                    right.ObjectType == (int)ObjectType.DocumentField && right.ObjectGuid == cabinetDef.ObjectId &&
                    right.AllowRights == (int)ATRightAllowRight.ViewDocField),
                rights.Any(right =>
                    right.ObjectType == (int)ObjectType.DocumentField && right.ObjectGuid == cabinetDef.ObjectId &&
                    right.AllowRights == (int)ATRightAllowRight.EditDocField))).ToList();

        logger.Information("Creating the result DTO");
        var resultDto = new ProcessPermissionsDTO(
            request.ProcessId,
            request.GroupId,
            processRights,
            folderFieldRights,
            allowedTemplates,
            documentFieldRights.AsReadOnly());

        logger.Information(
            "Successfully listed process rights for ProcessId {ProcessId} and GroupId {GroupId} by User {UserName}",
            request.ProcessId, request.GroupId, request.UserName);
        return new Response<ProcessPermissionsDTO>(resultDto, true);
    }

    public async Task<Response<QueuePermissionDTO>> ListQueuesRights(ListQueuesRightsRequest request)
    {
        logger.Information(
            "Starting to list queue rights for QueueId {QueueId} and GroupId {GroupId} by User {UserName}",
            request.QueueId, request.GroupId, request.UserName);

        var groupIdExists = await userRepository.AnyAsync(user => user.ObjectId == request.GroupId);
        if (!groupIdExists)
        {
            logger.Error($"User {request.UserName} requisted not existing group id {request.GroupId}");
            throw new NotFoundException($"The group id was not found {request.GroupId}");
        }

        logger.Information("Retrieving rights for GroupId {GroupId}", request.GroupId);
        var rights = await rightsRepository.ListAsync(right => right.UserGuid == request.GroupId);

        logger.Information("Retrieving queue details for QueueId {QueueId}", request.QueueId);
        var queue = await queueRepository.FindAsync(queue => queue.Id == request.QueueId);

        logger.Information("Retrieving buttons for QueueId {QueueId}", request.QueueId);
        var buttons = await buttonRepository.ListAsync(button => button.Queue.Id == request.QueueId);

        logger.Information("Processing queue rights specific logic");
        var queueRights = rights
            .Where(right => right.ObjectType == (int)ObjectType.Queue && right.ObjectGuid == queue.ObjectId)
            .Select(right => (ATRightAllowRight)right.AllowRights).ToArray();

        logger.Information("Processing button rights specific logic");
        var buttonRights = buttons.Select(button => new ButtonPermissionDTO
        {
            ButtonObjectId = button.ObjectId,
            ButtonName = button.ButtonName,
            AllowedToView = rights.Any(right =>
                right.ObjectType == (int)ObjectType.Button && right.ObjectGuid == button.ObjectId &&
                right.AllowRights == (int)ATRightAllowRight.ViewButton),
            AllowedToClick = rights.Any(right =>
                right.ObjectType == (int)ObjectType.Button && right.ObjectGuid == button.ObjectId &&
                right.AllowRights == (int)ATRightAllowRight.ClickButton)
        }).ToArray();

        logger.Information("Creating the result DTO");
        var resultDto = new QueuePermissionDTO
        {
            QueueObjectId = queue.ObjectId,
            GroupObjectId = request.GroupId,
            QueueRights = queueRights,
            ButtonPermissions = buttonRights
        };

        logger.Information(
            "Successfully listed queue rights for QueueId {QueueId} and GroupId {GroupId} by User {UserName}",
            request.QueueId, request.GroupId, request.UserName);
        return new Response<QueuePermissionDTO>(resultDto, true);
    }
}