package com.iqser.red.persistence.service.v1.external.api.impl.controller;

import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.ADD_COMMENT;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.DELETE_COMMENT;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.DELETE_MANUAL_REDACTION;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.DO_MANUAL_REDACTION;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.READ_MANUAL_REDACTIONS;

import java.util.List;
import java.util.Map;
import java.util.Set;

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import com.iqser.red.service.persistence.management.v1.processor.service.AccessControlService;
import com.iqser.red.service.persistence.management.v1.processor.service.CommentService;
import com.iqser.red.service.persistence.management.v1.processor.service.DossierManagementService;
import com.iqser.red.service.persistence.management.v1.processor.service.manualredactions.ManualRedactionMapper;
import com.iqser.red.service.persistence.management.v1.processor.service.manualredactions.ManualRedactionService;
import com.iqser.red.service.persistence.management.v1.processor.service.manualredactions.ManualRedactionUndoService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.AuditPersistenceService;
import com.iqser.red.service.persistence.service.v1.api.external.resource.ManualRedactionResource;
import com.iqser.red.service.persistence.service.v1.api.shared.model.AuditCategory;
import com.iqser.red.service.persistence.service.v1.api.shared.model.CommentResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.AddRedactionRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.CommentRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.Comments;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ForceRedactionRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.LegalBasisChangeRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ManualAddResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ManualRedactions;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.RecategorizationRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.RemoveRedactionRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ResizeRedactionRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.audit.AuditRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.AddCommentRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.AddRedactionRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.ForceRedactionRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.LegalBasisChangeRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.RecategorizationRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.RemoveRedactionRequestModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.manual.ResizeRedactionRequestModel;
import com.knecon.fforesight.keycloakcommons.security.KeycloakSecurity;

import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import lombok.experimental.FieldDefaults;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@RestController
@RequiredArgsConstructor
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
public class ManualRedactionController implements ManualRedactionResource {

    final static String FILE_ID = "fileId";
    final static String DOSSIER_ID = "dossierId";
    final static String ANNOTATION_ID = "annotationId";
    ManualRedactionService manualRedactionService;
    ManualRedactionUndoService manualRedactionUndoService;
    DossierManagementService dossierManagementService;
    AuditPersistenceService auditPersistenceService;
    AccessControlService accessControlService;
    ManualRedactionMapper manualRedactionMapper;
    CommentService commentService;


    @Override
    @PreAuthorize("hasAuthority('" + DELETE_MANUAL_REDACTION + "')")
    public void undo(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId, @RequestBody Set<String> annotationIds) {

        accessControlService.verifyFileIsNotApproved(dossierId, fileId);
        accessControlService.verifyUserIsApprover(dossierId);
        manualRedactionUndoService.undo(dossierId, fileId, annotationIds);

    }


    @Override
    @PreAuthorize("hasAuthority('" + DELETE_COMMENT + "')")
    public void undoComment(@PathVariable(DOSSIER_ID) String dossierId,
                            @PathVariable(FILE_ID) String fileId,
                            @PathVariable(ANNOTATION_ID) String annotationId,
                            @PathVariable(COMMENT_ID) String commentId) {

        accessControlService.verifyFileIsNotApproved(dossierId, fileId);
        accessControlService.verifyUserIsReviewerOrApprover(dossierId, fileId);

        auditPersistenceService.audit(AuditRequest.builder()
                .userId(KeycloakSecurity.getUserId())
                .objectId(fileId)
                .category(AuditCategory.DOCUMENT.name())
                .message("Comment was removed.")
                .details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, annotationId))
                .build());
        commentService.deleteComment(dossierId, fileId, List.of(Long.valueOf(commentId)));

    }


    @Override
    @PreAuthorize("hasAuthority('" + READ_MANUAL_REDACTIONS + "')")
    public ManualRedactions getManualRedactions(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId) {

        return manualRedactionService.getManualRedactions(fileId);
    }


    @Override
    @PreAuthorize("hasAuthority('" + READ_MANUAL_REDACTIONS + "')")
    public Comments getComments(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId) {

        return commentService.getComments(fileId);
    }


    @Override
    @PreAuthorize("hasAuthority('" + ADD_COMMENT + "')")
    public CommentResponse addComment(@PathVariable(DOSSIER_ID) String dossierId,
                                      @PathVariable(FILE_ID) String fileId,
                                      @PathVariable(ANNOTATION_ID) String annotationId,
                                      @RequestBody AddCommentRequestModel addCommentRequest) {

        accessControlService.verifyFileIsNotApproved(dossierId, fileId);
        accessControlService.verifyUserIsReviewerOrApprover(dossierId, fileId);

        var response = commentService.addComment(dossierId,
                fileId,
                annotationId,
                CommentRequest.builder().user(KeycloakSecurity.getUserId()).text(addCommentRequest.getText()).build());

        auditPersistenceService.audit(AuditRequest.builder()
                .userId(KeycloakSecurity.getUserId())
                .objectId(fileId)
                .category(AuditCategory.DOCUMENT.name())
                .message("Comment was added.")
                .details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, annotationId))
                .build());

        return new CommentResponse(String.valueOf(response.getId()));
    }


    @Override
    @PreAuthorize("hasAuthority('" + DO_MANUAL_REDACTION + "')")
    public List<ManualAddResponse> addRedactionBulk(@PathVariable(DOSSIER_ID) String dossierId,
                                                    @PathVariable(FILE_ID) String fileId,
                                                    @RequestBody Set<AddRedactionRequestModel> addRedactionRequests) {

        var dossier = dossierManagementService.getDossierById(dossierId, false, false);

        accessControlService.verifyFileIsNotApproved(dossierId, fileId);
        if (addRedactionRequests.stream().anyMatch(AddRedactionRequestModel::isAddToAllDossiers)) {
            accessControlService.verifyUserIsApprover(dossierId);
        } else {
            accessControlService.verifyUserIsMemberOrApprover(dossierId);
        }

        List<AddRedactionRequest> requests = manualRedactionMapper.toAddRedactionRequestList(dossierId, addRedactionRequests, dossier);

        List<ManualAddResponse> responseList = manualRedactionService.addAddRedaction(dossierId, fileId, requests);
        responseList.forEach(response -> auditPersistenceService.audit(AuditRequest.builder()
                .userId(KeycloakSecurity.getUserId())
                .objectId(fileId)
                .category(AuditCategory.DOCUMENT.name())
                .message("Manual redaction was added.")
                .details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, response.getAnnotationId()))
                .build()));

        return responseList;
    }


    @PreAuthorize("hasAuthority('" + DO_MANUAL_REDACTION + "')")
    public List<ManualAddResponse> removeRedactionBulk(@PathVariable(DOSSIER_ID) String dossierId,
                                                       @PathVariable(FILE_ID) String fileId,
                                                       @RequestBody Set<RemoveRedactionRequestModel> removeRedactionRequests) {

        var dossier = dossierManagementService.getDossierById(dossierId, false, false);
        accessControlService.verifyFileIsNotApproved(dossierId, fileId);
        if (removeRedactionRequests.stream().anyMatch(RemoveRedactionRequestModel::isRemoveFromAllDossiers)) {
            accessControlService.verifyUserIsApprover(dossierId);
        } else {
            accessControlService.verifyUserIsMemberOrApprover(dossierId);
        }

        List<RemoveRedactionRequest> requests = manualRedactionMapper.toRemoveRedactionRequestList(dossierId, fileId, dossier.getDossierTemplateId(), removeRedactionRequests);
        List<ManualAddResponse> responseList = manualRedactionService.addRemoveRedaction(dossierId, fileId, requests);

        responseList.forEach(response -> auditPersistenceService.audit(AuditRequest.builder()
                .userId(KeycloakSecurity.getUserId())
                .objectId(fileId)
                .category(AuditCategory.DOCUMENT.name())
                .message("Redaction was manually removed")
                .details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, response.getAnnotationId()))
                .build()));

        return responseList;
    }


    @PreAuthorize("hasAuthority('" + DO_MANUAL_REDACTION + "')")
    public List<ManualAddResponse> forceRedactionBulk(@PathVariable(DOSSIER_ID) String dossierId,
                                                      @PathVariable(FILE_ID) String fileId,
                                                      @RequestBody Set<ForceRedactionRequestModel> forceRedactionRequests) {

        accessControlService.verifyFileIsNotApproved(dossierId, fileId);
        accessControlService.verifyUserIsMemberOrApprover(dossierId);

        List<ForceRedactionRequest> requests = manualRedactionMapper.toForceRedactionRequestList(forceRedactionRequests);
        List<ManualAddResponse> responseList = manualRedactionService.addForceRedaction(dossierId, fileId, requests);

        responseList.forEach(response -> auditPersistenceService.audit(AuditRequest.builder()
                .userId(KeycloakSecurity.getUserId())
                .objectId(fileId)
                .category(AuditCategory.DOCUMENT.name())
                .message("Skipped redaction was forced to be redacted")
                .details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, response.getAnnotationId()))
                .build()));

        return responseList;
    }


    @PreAuthorize("hasAuthority('" + DO_MANUAL_REDACTION + "')")
    public List<ManualAddResponse> legalBasisChangeBulk(@PathVariable(DOSSIER_ID) String dossierId,
                                                        @PathVariable(FILE_ID) String fileId,
                                                        @RequestBody Set<LegalBasisChangeRequestModel> legalBasisChangeRequests) {

        accessControlService.verifyFileIsNotApproved(dossierId, fileId);
        accessControlService.verifyUserIsMemberOrApprover(dossierId);

        List<LegalBasisChangeRequest> requests = manualRedactionMapper.toLegalBasisChangeRequestList(legalBasisChangeRequests);

        List<ManualAddResponse> responseList = manualRedactionService.addLegalBasisChange(dossierId, fileId, requests);

        responseList.forEach(response -> auditPersistenceService.audit(AuditRequest.builder()
                .userId(KeycloakSecurity.getUserId())
                .objectId(fileId)
                .category(AuditCategory.DOCUMENT.name())
                .message("Legal basis reason was changed")
                .details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, response.getAnnotationId()))
                .build()));

        return responseList;
    }


    @PreAuthorize("hasAuthority('" + DO_MANUAL_REDACTION + "')")
    public List<ManualAddResponse> recategorizeBulk(@PathVariable(DOSSIER_ID) String dossierId,
                                                    @PathVariable(FILE_ID) String fileId,
                                                    @RequestBody Set<RecategorizationRequestModel> recategorizationRequests) {

        var dossier = dossierManagementService.getDossierById(dossierId, false, false);
        accessControlService.verifyFileIsNotApproved(dossierId, fileId);
        accessControlService.verifyUserIsMemberOrApprover(dossierId);

        List<RecategorizationRequest> requests = manualRedactionMapper.toRecategorizationRequestList(dossierId, fileId, dossier.getDossierTemplateId(), recategorizationRequests);

        List<ManualAddResponse> responseList = manualRedactionService.addRecategorization(dossierId, fileId, requests);

        responseList.forEach(response -> auditPersistenceService.audit(AuditRequest.builder()
                .userId(KeycloakSecurity.getUserId())
                .objectId(fileId)
                .category(AuditCategory.DOCUMENT.name())
                .message("Entity was recategorized.")
                .details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, response.getAnnotationId()))
                .build()));

        return responseList;
    }


    @PreAuthorize("hasAuthority('" + DO_MANUAL_REDACTION + "')")
    public List<ManualAddResponse> resizeRedactionBulk(@PathVariable(DOSSIER_ID) String dossierId,
                                                       @PathVariable(FILE_ID) String fileId,
                                                       @RequestBody Set<ResizeRedactionRequestModel> resizeRedactionRequests) {

        accessControlService.verifyFileIsNotApproved(dossierId, fileId);
        accessControlService.verifyUserIsMemberOrApprover(dossierId);

        List<ResizeRedactionRequest> requests = manualRedactionMapper.toResizeRedactionRequestList(resizeRedactionRequests);
        List<ManualAddResponse> responseList = manualRedactionService.addResizeRedaction(dossierId, fileId, requests);

        responseList.forEach(response -> auditPersistenceService.audit(AuditRequest.builder()
                .userId(KeycloakSecurity.getUserId())
                .objectId(fileId)
                .category(AuditCategory.DOCUMENT.name())
                .message("Skipped redaction was resized to be redacted")
                .details(Map.of(DOSSIER_ID, dossierId, FILE_ID, fileId, ANNOTATION_ID, response.getAnnotationId()))
                .build()));

        return responseList;
    }

}
