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

import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.DELETE_REPORT_TEMPLATE;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.DOWNLOAD_REPORT_TEMPLATE;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.GET_REPORT_TEMPLATES;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.UPLOAD_REPORT_TEMPLATE;
import static com.iqser.red.service.persistence.management.v1.processor.service.FeignExceptionHandler.processFeignException;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.commons.io.IOUtils;
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.RequestParam;
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.service.FileManagementStorageService;
import com.knecon.fforesight.keycloakcommons.security.KeycloakSecurity;
import com.iqser.red.service.persistence.management.v1.processor.client.redactionreportservice.PlaceholderClient;
import com.iqser.red.service.persistence.management.v1.processor.client.redactionreportservice.ReportTemplatePlaceholderClient;
import com.iqser.red.service.persistence.management.v1.processor.exception.BadRequestException;
import com.iqser.red.service.persistence.management.v1.processor.exception.NotFoundException;
import com.iqser.red.service.persistence.management.v1.processor.service.ReportTemplateService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.AuditPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierAttributeConfigPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.FileAttributeConfigPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.ReportTemplatePersistenceService;
import com.knecon.fforesight.databasetenantcommons.providers.utils.MagicConverter;
import com.iqser.red.service.persistence.management.v1.processor.utils.StringEncodingUtils;
import com.iqser.red.service.persistence.service.v1.api.external.resource.ReportTemplateResource;
import com.iqser.red.service.persistence.service.v1.api.shared.model.AuditCategory;
import com.iqser.red.service.persistence.service.v1.api.shared.model.PlaceholdersResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.ReportTemplateUpdateRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.audit.AuditRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.common.JSONPrimitive;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.ReportTemplate;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.ReportTemplateUploadRequest;
import com.iqser.red.storage.commons.exception.StorageObjectDoesNotExist;
import com.iqser.red.storage.commons.service.StorageService;
import com.knecon.fforesight.tenantcommons.TenantContext;

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

@Slf4j
@RestController
@RequiredArgsConstructor
public class ReportTemplateController implements ReportTemplateResource {

    private final ReportTemplatePlaceholderClient reportTemplatePlaceholderClient;
    private final PlaceholderClient placeholderClient;
    private final AuditPersistenceService auditPersistenceService;
    private final DossierAttributeConfigPersistenceService dossierAttributeConfigPersistenceService;
    private final FileAttributeConfigPersistenceService fileAttributeConfigPersistenceService;
    private final StorageService storageService;
    private final ReportTemplatePersistenceService reportTemplatePersistenceService;
    private final ReportTemplateService reportTemplateService;
    private final FileManagementStorageService fileManagementStorageService;



    @Override
    @PreAuthorize("hasAuthority('" + GET_REPORT_TEMPLATES + "')")
    public List<ReportTemplate> getReportTemplatesByPlaceholder(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId, @RequestBody JSONPrimitive<String> placeholder) {

        try {
            return reportTemplatePlaceholderClient.getReportTemplatesByPlaceholder(dossierTemplateId, placeholder);
        } catch (FeignException e) {
            throw processFeignException(e);
        }
    }


    @Override
    @PreAuthorize("hasAuthority('" + UPLOAD_REPORT_TEMPLATE + "')")
    public ReportTemplate uploadTemplate(@RequestPart(name = "file") MultipartFile file,
                                         @PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId,
                                         @RequestParam(value = MULTI_FILE_REPORT, required = false, defaultValue = "false") boolean multiFileReport,
                                         @RequestParam(value = ACTIVE_BY_DEFAULT, required = false, defaultValue = "false") boolean activeByDefault) {

        try {
            if (file.getOriginalFilename() != null) {

                ReportTemplateUploadRequest reportTemplateUploadRequest = ReportTemplateUploadRequest.builder()
                        .template(file.getBytes())
                        .fileName(file.getOriginalFilename())
                        .dossierTemplateId(dossierTemplateId)
                        .activeByDefault(activeByDefault)
                        .multiFileReport(multiFileReport)
                        .build();
                var reportTemplate = reportTemplateService.uploadTemplate(reportTemplateUploadRequest);
                auditPersistenceService.audit(AuditRequest.builder()
                        .userId(KeycloakSecurity.getUserId())
                        .objectId(reportTemplate.getTemplateId())
                        .category(AuditCategory.DOSSIER_TEMPLATE.name())
                        .message("Report template was uploaded.")
                        .details(Map.of("DossierTemplateId", dossierTemplateId))
                        .build());
                return reportTemplate;
            } else {
                throw new BadRequestException("Could not upload file, no filename provided.");
            }
        } catch (IOException e) {
            throw new BadRequestException(e.getMessage(), e);
        }

    }


    @Override
    @PreAuthorize("hasAuthority('" + GET_REPORT_TEMPLATES + "')")
    public List<ReportTemplate> getAvailableReportTemplates(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId) {

        return MagicConverter.convert(reportTemplatePersistenceService.findByDossierTemplateId(dossierTemplateId), ReportTemplate.class);
    }


    @Override
    @PreAuthorize("hasAuthority('" + DOWNLOAD_REPORT_TEMPLATE + "')")
    public ResponseEntity<?> downloadReportTemplate(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId, @PathVariable(TEMPLATE_ID) String templateId) {

        try {
            var reportTemplate = reportTemplatePersistenceService.find(templateId);
            InputStream inputStream = fileManagementStorageService.getObject(TenantContext.getTenantId(), reportTemplate.getStorageId());
            byte[] file = IOUtils.toByteArray(inputStream);
            inputStream.close();
            return getResponseEntity(file, reportTemplate.getFileName());
        } catch (StorageObjectDoesNotExist | IOException e) {
            throw new NotFoundException("Template does not exist");
        }
    }


    private ResponseEntity<?> getResponseEntity(byte[] file, String filename) {

        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM);

        if (filename != null) {
            httpHeaders.add("Content-Disposition", "attachment; filename*=utf-8''" + StringEncodingUtils.urlEncode(filename));
        }
        InputStream is = new ByteArrayInputStream(file);

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

    }


    @Override
    @PreAuthorize("hasAuthority('" + DELETE_REPORT_TEMPLATE + "')")
    public void deleteTemplate(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId, @PathVariable(TEMPLATE_ID) String templateId) {

        var storageId = reportTemplatePersistenceService.find(templateId).getStorageId();
        storageService.deleteObject(TenantContext.getTenantId(), storageId);
        reportTemplatePersistenceService.delete(templateId);
        auditPersistenceService.audit(AuditRequest.builder()
                .userId(KeycloakSecurity.getUserId())
                .objectId(templateId)
                .category(AuditCategory.DOSSIER_TEMPLATE.name())
                .message("Report template was deleted.")
                .details(Map.of("DossierTemplateId", dossierTemplateId))
                .build());
    }


    @Override
    public PlaceholdersResponse getAvailablePlaceholders(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId) {

        PlaceholdersResponse availablePlaceholders = new PlaceholdersResponse();

        List<String> dossierPlaceholders = new ArrayList<>();
        for (var dossierAttributeConfig : dossierAttributeConfigPersistenceService.getDossierAttributes(dossierTemplateId)) {
            dossierPlaceholders.add(dossierAttributeConfig.getPlaceholder());
        }
        availablePlaceholders.setDossierAttributePlaceholders(dossierPlaceholders);

        List<String> filePlaceholders = new ArrayList<>();
        for (var fileAttributeConfig : fileAttributeConfigPersistenceService.getFileAttributes(dossierTemplateId)) {
            filePlaceholders.add(fileAttributeConfig.getPlaceholder());
        }
        availablePlaceholders.setFileAttributePlaceholders(filePlaceholders);

        List<String> generalPlaceholders = placeholderClient.getPlaceholders();
        availablePlaceholders.setGeneralPlaceholders(generalPlaceholders);

        return availablePlaceholders;

    }


    @PreAuthorize("hasAuthority('" + UPLOAD_REPORT_TEMPLATE + "')")
    public void updateTemplate(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId,
                               @PathVariable(TEMPLATE_ID) String templateId,
                               @RequestBody ReportTemplateUpdateRequest updateRequest) {

        reportTemplatePersistenceService.updateTemplate(dossierTemplateId, templateId, updateRequest);
    }

}
