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

import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.READ_REDACTION_LOG;
import static com.iqser.red.service.persistence.management.v1.processor.service.FeignExceptionHandler.processFeignException;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

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.RestController;

import com.iqser.red.service.persistence.management.v1.processor.service.FileManagementStorageService;
import com.iqser.red.service.persistence.management.v1.processor.service.FileStatusService;
import com.iqser.red.service.persistence.management.v1.processor.service.RedactionLogService;
import com.iqser.red.service.persistence.management.v1.processor.utils.StringEncodingUtils;
import com.iqser.red.service.persistence.service.v1.api.external.resource.RedactionLogResource;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileType;
import com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlog.FilteredRedactionLogRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlog.RedactionLog;
import com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlog.section.SectionGrid;
import com.iqser.red.storage.commons.exception.StorageObjectDoesNotExist;
import com.knecon.fforesight.tenantcommons.TenantContext;

import feign.FeignException;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;

@RestController
@RequiredArgsConstructor
@Deprecated(forRemoval = true)
public class RedactionLogController implements RedactionLogResource {

    private final RedactionLogService redactionLogService;
    private final FileStatusService fileStatusService;
    private final FileManagementStorageService fileManagementStorageService;


    @Deprecated(forRemoval = true)
    @PreAuthorize("hasAuthority('" + READ_REDACTION_LOG + "')")
    public RedactionLog getRedactionLog(@PathVariable(DOSSIER_ID) String dossierId,
                                        @PathVariable(FILE_ID) String fileId,
                                        @RequestParam(value = "excludedType", required = false) List<String> excludedTypes,
                                        @RequestParam(value = "withManualRedactions", required = false, defaultValue = "true") boolean withManualRedactions,
                                        @RequestParam(value = "includeFalsePositives", required = false, defaultValue = "false") boolean includeFalsePositives) {

        try {
            return redactionLogService.getRedactionLog(dossierId, fileId, excludedTypes);
        } catch (FeignException e) {
            throw processFeignException(e);
        }
    }


    @Deprecated(forRemoval = true)
    @PreAuthorize("hasAuthority('" + READ_REDACTION_LOG + "')")
    public SectionGrid getSectionGrid(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId) {

        try {
            return redactionLogService.getSectionGrid(dossierId, fileId);
        } catch (FeignException e) {
            throw processFeignException(e);
        }
    }


    @Deprecated(forRemoval = true)
    @SneakyThrows
    @PreAuthorize("hasAuthority('" + READ_REDACTION_LOG + "')")
    public ResponseEntity<?> getSectionText(@PathVariable(DOSSIER_ID) String dossierId, @PathVariable(FILE_ID) String fileId) {

        try {
            return buildZipFileResponseEntity(fileId, dossierId, FileType.TEXT);
        } catch (FeignException e) {
            throw processFeignException(e);
        }
    }


    private ResponseEntity<byte[]> buildZipFileResponseEntity(String fileId, String dossierId, FileType fileType) throws IOException {

        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setContentType(MediaType.parseMediaType("application/zip"));

        var fileStatus = fileStatusService.getStatus(fileId);
        String filename = fileStatus.getFilename();
        if (filename != null) {
            var index = filename.lastIndexOf(".");
            String prefix = filename.substring(0, index);
            filename = prefix + ".json";
            httpHeaders.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename*=utf-8''" + StringEncodingUtils.urlEncode(prefix) + ".zip");
        }

        byte[] zipBytes = getZippedBytes(dossierId, fileId, filename, fileType);
        httpHeaders.setContentLength(zipBytes.length);
        return new ResponseEntity<>(zipBytes, httpHeaders, HttpStatus.OK);
    }


    @Deprecated(forRemoval = true)
    @PreAuthorize("hasAuthority('" + READ_REDACTION_LOG + "')")
    public RedactionLog getFilteredRedactionLog(@PathVariable(DOSSIER_ID) String dossierId,
                                                @PathVariable(FILE_ID) String fileId,
                                                @RequestBody FilteredRedactionLogRequest filteredRedactionLogRequest) {

        try {

            return redactionLogService.getFilteredRedactionLog(dossierId, fileId, filteredRedactionLogRequest);

        } catch (FeignException e) {
            throw processFeignException(e);
        }

    }


    private byte[] getZippedBytes(String dossierId, String fileId, String filename, FileType fileType) throws IOException {

        try {
            String objectId = dossierId + "/" + fileId + "." + fileType.name() + fileType.getExtension();

            try (var inputStream = fileManagementStorageService.getObject(TenantContext.getTenantId(), objectId)) {
                byte[] input = inputStream.readAllBytes();
                inputStream.close();
                return zipBytes(filename, input);
            }

        } catch (StorageObjectDoesNotExist e) {
            throw new RuntimeException(String.format("%s is not available", fileType.name()), e);
        }
    }


    public static byte[] zipBytes(String filename, byte[] input) throws IOException {

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ZipOutputStream zos = new ZipOutputStream(baos);
        ZipEntry entry = new ZipEntry(filename);
        entry.setSize(input.length);
        zos.putNextEntry(entry);
        zos.write(input);
        zos.closeEntry();
        zos.close();
        return baos.toByteArray();
    }

}
