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

namespace IWS5.Services;

public class DocumentTemplateService(
    IRightsRepository rightsRepository,
    IDocumentCabinetRepository documentCabinetRepository,
    IProcessDocumentTemplateRightsService processDocumentTemplateRightsService,
    IValidator<CreateDocumentTemplateRequest> createDocumentTemplateRequestValidator,
    IValidator<UpdateDocumentTemplateRequest> updateDocumentTemplateRequestValidator,
    ILogger logger) : IDocumentTemplateService
{
    public async Task<Response<IReadOnlyCollection<DocumentTemplateListItemResponseDTO>>> ListDocumentTemplates(
        BaseRequest request)
    {
        logger.Information("Starting ListDocumentTemplates for User {UserName}", request.UserName);
        var data = await documentCabinetRepository.GetProcessDefaultDocumentTemplate();

        var result = data
            .Select(template => new DocumentTemplateListItemResponseDTO(
                CabinetId: template.CabinetId,
                CabinetName: template.CabinetName,
                CabinetDescription: template.CabinetDescription,
                TemplateTypeId: (ATTemplateType)template.TemplateTypeId,
                TemplateTypeName: template.TemplateType.Name,
                CreatedDate: template.CreateDate,
                ModifiedDate: template.ModifyDate
            )).ToList().AsReadOnly();

        logger.Information("Successfully retrieved {DocumentTemplatesCount} document templates for User {UserName}",
            result.Count,
            request.UserName);

        return new Response<IReadOnlyCollection<DocumentTemplateListItemResponseDTO>>(result, true);
    }

    public async Task<Response<List<DocumentTemplateLitimResponseDTO>>> GetLimitDocumentTemplatesDataById(
        GetDocumentLimitTemplatesRequest request)
    {
        logger.Information("Retrieving all limit document templates");
        var limitTemplatesData = await processDocumentTemplateRightsService.ListProcessLimitTemplate(request.ProcessId);

        logger.Information("Retrieving all rights for document fields");
        var rights = await rightsRepository.ListAsync(right => right.UserGuid == request.GroupId
                                                               && right.ObjectType == (int)ObjectType.DocumentField);

        logger.Information("Retrieving all rights for document fields");
        var templateWithFields = await documentCabinetRepository.GetDocumentTemplatesByIds(limitTemplatesData
            .Select(t => t.DocumentTemplateObjectId).ToArray());

        logger.Information("Processing allowed templates specific logic");
        var limitTemplates = templateWithFields
            .Select(template => new DocumentTemplateLitimResponseDTO()
            {
                TemplateId = template.ObjectId,
                TemplateName = template.CabinetName,
                Fields = template.AtdocumentCabinetDefs.Select(cabintDef => new ProcessFieldPermissionsDTO(
                    cabintDef.FieldId,
                    cabintDef.ObjectId,
                    cabintDef.FieldName,
                    rights.Any(right =>
                        right.ObjectType == (int)ObjectType.DocumentField && right.ObjectGuid == cabintDef.ObjectId &&
                        right.AllowRights == (int)ATRightAllowRight.ViewDocField),
                    rights.Any(right =>
                        right.ObjectType == (int)ObjectType.DocumentField && right.ObjectGuid == cabintDef.ObjectId &&
                        right.AllowRights == (int)ATRightAllowRight.EditDocField))).ToArray()
            })
            .ToList();

        logger.Information("Successfully retrieved {DocumentTemplatesCount} document templates for User {UserName}",
            limitTemplates.Count, request.UserName);

        if (limitTemplates == null)
        {
            logger.Error("No limit document template were found");
            return new Response<List<DocumentTemplateLitimResponseDTO>>(null, false,
                "Limit document templates were not found.");
        }
        else
        {
            return new Response<List<DocumentTemplateLitimResponseDTO>>(limitTemplates, true);
        }
    }

    public async Task<Response<DocumentTemplateResponseDTO>> GetDocumentTemplateDataById(
        GetDocumentTemplateRequest request)
    {
        logger.Information("Starting GetDocumentTemplateDataById for User {UserName}", request.UserName);

        var data = await documentCabinetRepository.GetDocumentTemplateById(request.TemplateId);

        if (data == null)
        {
            logger.Error("No document template was found by {templateId} for User {UserName}", request.TemplateId,
                request.UserName);
            return new Response<DocumentTemplateResponseDTO>(null, false, "Document template not found.");
        }

        var result = new DocumentTemplateResponseDTO(
            TemplateId: data.CabinetId,
            TemplateName: data.CabinetName,
            TemplateTypeId: (ATTemplateType)data.TemplateTypeId,
            TemplateTypeName: data.TemplateType.Name,
            TemplateDescription: data.CabinetDescription,
            ConnectionId: data.DbConnectionId,
            DatabaseName: data.DbName,
            Fields: data.AtdocumentCabinetDefs.Select(templateDef => new DocumentTemplateFieldListItemDTO(
                Id: templateDef.FieldId,
                Name: templateDef.FieldName,
                Description: templateDef.FieldDescription,
                DataType: templateDef.FieldDataType,
                Size: templateDef.FieldSize,
                DefaultValue: templateDef.DefaultValue,
                Display: templateDef.Display
            )).ToArray()
        );

        logger.Information(
            "Successfully retrieved {DocumentTemplate} document template for User {UserName} by template Id {templateId}",
            result, request.UserName);
        return new Response<DocumentTemplateResponseDTO>(result, true);
    }

    public async Task<Response<DocumentTemplateFieldListResponseDTO>> GetDocumentTemplateFieldsById(
        GetDocumentTemplateRequest request)
    {
        logger.Information("Starting GetDocumentTemplateFieldsById for User {UserName} and TemplateId {TemplateId}",
            request.UserName, request.TemplateId);

        logger.Information("Retrieving allowed template fields for User {UserName} and TemplateId {TemplateId}",
            request.UserName, request.TemplateId);
        var cabinet = await documentCabinetRepository.GetAllowedTemplateFields(request.UserName, request.TemplateId);
        var fields = cabinet.AtdocumentCabinetDefs.Select(cabinetDef => cabinetDef.ObjectId).ToArray();
        logger.Information("Retrieved allowed template fields for TemplateId {TemplateId}", request.TemplateId);

        logger.Information("Retrieving edit rights for User {UserName}", request.UserName);
        var editRights = await rightsRepository.ListUserRelatedRights(request.UserName, ObjectType.DocumentField,
            ATRightAllowRight.EditDocField, fields);
        var fieldsAvailableToEdit = editRights.Select(right => right.ObjectGuid).ToArray();
        logger.Information("Retrieved {EditRightsCount} edit rights for User {UserName}", editRights.Count,
            request.UserName);

        logger.Information("Processing template fields for TemplateId {TemplateId}", request.TemplateId);

        var result = new DocumentTemplateFieldListResponseDTO
        {
            TemplateId = cabinet.CabinetId,
            TemplateName = cabinet.CabinetName,
            Fields = cabinet.AtdocumentCabinetDefs.Select(cabinetDef => new CabinetFieldDto
            {
                FieldId = cabinetDef.FieldId,
                FieldName = cabinetDef.FieldName,
                FieldType = (ATDocumentFieldType)cabinetDef.FieldDataTypeId.Value,
                FieldTypeName = ((ATDocumentFieldType)cabinetDef.FieldDataTypeId.Value).ToString(),
                FieldSize = cabinetDef.FieldSize,
                FieldValue = cabinetDef.DefaultValue ?? string.Empty,
                IsEditable = fieldsAvailableToEdit.Contains(cabinetDef.ObjectId)
            }).ToArray()
        };
        logger.Information("Processed {FieldsCount} fields for TemplateId {TemplateId}", result.Fields.Length,
            request.TemplateId);

        logger.Information(
            "Successfully completed GetDocumentTemplateFieldsById for User {UserName} and TemplateId {TemplateId}",
            request.UserName, request.TemplateId);
        return new Response<DocumentTemplateFieldListResponseDTO>(result, true);
    }

    public async Task CreateDocumentTemplate(CreateDocumentTemplateRequest request)
    {
        await createDocumentTemplateRequestValidator.ValidateAndThrowAsync(request);
        await documentCabinetRepository.CreateDocumentTemplate(request);
    }

    public async Task UpdateDocumentCabinet(UpdateDocumentTemplateRequest request)
    {
        await updateDocumentTemplateRequestValidator.ValidateAndThrowAsync(request);
        await documentCabinetRepository.UpdateDocumentTemplate(request);
    }
}