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

import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.READ_RULES;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.WRITE_RULES;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;

import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
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.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import com.iqser.red.service.persistence.management.v1.processor.exception.BadRequestException;
import com.iqser.red.service.persistence.management.v1.processor.exception.FileUploadException;
import com.iqser.red.service.persistence.management.v1.processor.service.RulesValidationService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.AuditPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.RulesPersistenceService;
import com.iqser.red.service.persistence.service.v1.api.external.resource.RulesResource;
import com.iqser.red.service.persistence.service.v1.api.shared.model.AuditCategory;
import com.iqser.red.service.persistence.service.v1.api.shared.model.RuleFileType;
import com.iqser.red.service.persistence.service.v1.api.shared.model.audit.AuditRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.rules.RuleSyntaxErrorMessage;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.rules.Rules;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.rules.RulesUploadRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.rules.RulesUploadRequestModel;
import com.iqser.red.service.redaction.v1.model.DroolsSyntaxValidation;
import com.knecon.fforesight.keycloakcommons.security.KeycloakSecurity;

import feign.FeignException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@RestController
@RequiredArgsConstructor
public class RulesController implements RulesResource {

    private static final String DOWNLOAD_FILE_NAME = "rules.drl";

    private final RulesPersistenceService rulesPersistenceService;
    private final RulesValidationService rulesValidationService;
    private final AuditPersistenceService auditPersistenceService;


    @Override
    @PreAuthorize("hasAuthority('" + WRITE_RULES + "')")
    public ResponseEntity<?> upload(@RequestBody RulesUploadRequestModel rules) {

        RulesUploadRequest rulesUploadRequest = RulesUploadRequest.fromModel(rules);
        try {
            DroolsSyntaxValidation droolsSyntaxValidation = rulesValidationService.validateRules(rulesUploadRequest.getRuleFileType(), rulesUploadRequest.getRules());
            if (!droolsSyntaxValidation.isCompiled()) {
                var rulesSyntaxErrorMessages = droolsSyntaxValidation.getDroolsSyntaxErrorMessages()
                        .stream()
                        .map(errorMessage -> RuleSyntaxErrorMessage.builder()
                                .line(errorMessage.getLine())
                                .column(errorMessage.getColumn())
                                .message(errorMessage.getMessage())
                                .build())
                        .toList();
                return new ResponseEntity<>(rulesSyntaxErrorMessages, HttpStatus.BAD_REQUEST);
            }
        } catch (FeignException e) {
            if (e.status() == HttpStatus.BAD_REQUEST.value()) {
                throw new BadRequestException("The provided rule string is not a valid drools rule file!");
            }
        }
        rulesPersistenceService.setRules(rulesUploadRequest.getRules(), rulesUploadRequest.getDossierTemplateId(), rulesUploadRequest.getRuleFileType());

        auditPersistenceService.audit(AuditRequest.builder()
                .userId(KeycloakSecurity.getUserId())
                .objectId(rulesUploadRequest.getDossierTemplateId())
                .category(AuditCategory.DOSSIER_TEMPLATE.name())
                .message(String.format("%s Rules have been updated", rulesUploadRequest.getRuleFileType()))
                .build());

        return ResponseEntity.ok().build();
    }


    @Override
    @PreAuthorize("hasAuthority('" + READ_RULES + "')")
    public Rules download(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId) {

        return download(dossierTemplateId, RuleFileType.ENTITY);
    }


    @Override
    @PreAuthorize("hasAuthority('" + READ_RULES + "')")
    public Rules download(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId, @PathVariable(RULE_FILE_TYPE_PARAMETER_NAME) RuleFileType ruleFileType) {

        var ruleEntity = rulesPersistenceService.getRules(dossierTemplateId, ruleFileType);
        return new Rules(ruleEntity.getValue(), dossierTemplateId, ruleEntity.isTimeoutDetected());

    }


    @Override
    @PreAuthorize("hasAuthority('" + WRITE_RULES + "')")
    public ResponseEntity<?> uploadFile(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId, @RequestPart(name = "file") MultipartFile file) {

        return uploadFile(dossierTemplateId, RuleFileType.ENTITY, file);
    }


    @Override
    @PreAuthorize("hasAuthority('" + WRITE_RULES + "')")
    public ResponseEntity<?> uploadFile(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId,
                                        @PathVariable(RULE_FILE_TYPE_PARAMETER_NAME) RuleFileType ruleFileType,
                                        @RequestPart(name = "file") MultipartFile file) {

        try {
            return upload(new RulesUploadRequestModel(new String(file.getBytes(), StandardCharsets.UTF_8), dossierTemplateId, ruleFileType));
        } catch (IOException e) {
            throw new FileUploadException("Could not upload file.", e);
        }
    }


    @Override
    @PreAuthorize("hasAuthority('" + READ_RULES + "')")
    public ResponseEntity<?> downloadFile(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId) {

        return downloadFile(dossierTemplateId, RuleFileType.ENTITY);
    }


    @Override
    @PreAuthorize("hasAuthority('" + READ_RULES + "')")
    public ResponseEntity<?> downloadFile(@PathVariable(DOSSIER_TEMPLATE_PARAMETER_NAME) String dossierTemplateId,
                                          @PathVariable(RULE_FILE_TYPE_PARAMETER_NAME) RuleFileType ruleFileType) {

        byte[] data = download(dossierTemplateId).getRules().getBytes(StandardCharsets.UTF_8);
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setContentType(MediaType.TEXT_PLAIN);

        httpHeaders.add("Content-Disposition", "attachment; filename*=utf-8\"" + ruleFileType.name() + "_" + DOWNLOAD_FILE_NAME + "\"");
        InputStream is = new ByteArrayInputStream(data);

        return new ResponseEntity<>(new InputStreamResource(is), httpHeaders, HttpStatus.OK);
    }

}
