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

import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.IMPORT_FILES;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.USE_SUPPORT_CONTROLLER;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.stream.Collectors;

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 org.springframework.web.multipart.MultipartFile;

import com.iqser.red.service.persistence.management.v1.processor.dataexchange.service.DatasetExchangeService;
import com.iqser.red.service.persistence.management.v1.processor.exception.BadRequestException;
import com.iqser.red.service.persistence.management.v1.processor.dataexchange.service.FileExchangeImportService;
import com.iqser.red.service.persistence.management.v1.processor.migration.MigrationController;
import com.iqser.red.service.persistence.management.v1.processor.service.FileStatusManagementService;
import com.iqser.red.service.persistence.management.v1.processor.service.FileStatusMapper;
import com.iqser.red.service.persistence.management.v1.processor.service.ReanalysisService;
import com.iqser.red.service.persistence.management.v1.processor.dataexchange.service.FileExchangeExportService;
import com.iqser.red.service.persistence.service.v1.api.shared.model.ImportResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.ReanalysisSettings;
import com.iqser.red.service.persistence.service.v1.api.external.resource.SupportResource;
import com.iqser.red.service.persistence.service.v1.api.shared.model.DownloadResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.FileStatus;
import com.iqser.red.service.persistence.service.v1.api.shared.model.FileStatusFilter;
import com.iqser.red.service.persistence.service.v1.api.shared.model.FileExchangeExportRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.ReanalyzeFilesResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileModel;
import com.knecon.fforesight.keycloakcommons.security.KeycloakSecurity;

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

@Slf4j
@RestController
@RequiredArgsConstructor
@PreAuthorize("hasAuthority('" + USE_SUPPORT_CONTROLLER + "')")
public class SupportController implements SupportResource {

    private final ReanalysisService reanalysisService;
    private final FileStatusManagementService fileStatusManagementService;
    private final FileExchangeExportService fileExchangeExportService;
    private final FileExchangeImportService fileExchangeImportService;
    private final DatasetExchangeService datasetExchangeService;


    @Override
    public ReanalyzeFilesResponse reanalyzeFiles(String dossierTemplateId, ReanalysisSettings reanalysisSettings) {

        return new ReanalyzeFilesResponse(reanalysisService.reanalyzeTemplate(dossierTemplateId, reanalysisSettings));
    }


    @Override
    public void reanalyzeAllRelevantErrorFiles(@RequestParam(value = FULL_REANALYSIS_PARAM, required = false, defaultValue = FALSE) boolean repeatStructureAnalysis,
                                               @RequestParam(value = RUN_OCR_PARAM, required = false, defaultValue = FALSE) boolean runOcr) {

        reanalysisService.reanalyzeAllRelevantErrorFiles(repeatStructureAnalysis, runOcr);
    }


    @Override
    public void reanalyzeErrorFilesBulkForDossier(@PathVariable(DOSSIER_ID) String dossierId,
                                                  @RequestBody List<String> fileIds,
                                                  @RequestParam(value = FULL_REANALYSIS_PARAM, required = false, defaultValue = FALSE) boolean repeatStructureAnalysis,
                                                  @RequestParam(value = RUN_OCR_PARAM, required = false, defaultValue = FALSE) boolean runOcr) {

        reanalysisService.reanalyzeGivenErrorFilesInDossier(dossierId, new HashSet<>(fileIds), repeatStructureAnalysis, runOcr);

    }


    private static Predicate<FileModel> matchesStatusFilters(FileStatusFilter fileStatusFilter) {

        FileStatusFilter filter = Optional.ofNullable(fileStatusFilter)
                .orElseGet(FileStatusFilter::new);

        if (filter.getProcessingStatusList() == null) {
            filter.setProcessingStatusList(new ArrayList<>());
        }

        if (filter.getWorkflowStatusList() == null) {
            filter.setWorkflowStatusList(new ArrayList<>());
        }

        return fileStatus -> (filter.getProcessingStatusList().isEmpty() || filter.getProcessingStatusList().contains(fileStatus.getProcessingStatus()))
                             && (filter.getWorkflowStatusList().isEmpty() || filter.getWorkflowStatusList().contains(fileStatus.getWorkflowStatus()))
                             && (filter.isIncludeSoftDeletedFiles() || fileStatus.getDeleted() == null)
                             && (filter.isIncludeHardDeletedFiles() || fileStatus.getHardDeletedTime() == null);
    }


    @Override
    public List<FileStatus> getFileStatus(@RequestBody FileStatusFilter fileStatusFilter) {

        return fileStatusManagementService.getAllFileStatuses()
                .stream()
                .filter(matchesStatusFilters(fileStatusFilter))
                .map(FileStatusMapper::toFileStatus)
                .collect(Collectors.toList());
    }


    @Override
    public List<FileStatus> getFileStatusForDossierTemplate(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId, @RequestBody FileStatusFilter fileStatusFilter) {

        return fileStatusManagementService.getAllDossierTemplateStatus(dossierTemplateId)
                .stream()
                .filter(matchesStatusFilters(fileStatusFilter))
                .map(FileStatusMapper::toFileStatus)
                .collect(Collectors.toList());
    }


    @Override
    public List<FileStatus> exportFiles(@PathVariable(DOSSIER_ID) String dossierId, @RequestBody FileStatusFilter fileStatusFilter) {

        return fileStatusManagementService.getAllDossierStatus(dossierId)
                .stream()
                .filter(matchesStatusFilters(fileStatusFilter))
                .map(FileStatusMapper::toFileStatus)
                .collect(Collectors.toList());
    }


    @Override
    public DownloadResponse exportDataset(@PathVariable(DOSSIER_TEMPLATE_ID) String dossierTemplateId) {

        return datasetExchangeService.prepareExport(dossierTemplateId);
    }


    @Override
    public DownloadResponse exportFiles(String dossierTemplateId, FileExchangeExportRequest exportRequest) {

        return fileExchangeExportService.prepareExportDownload(dossierTemplateId, exportRequest);
    }


    @Override
    @PreAuthorize("hasAuthority('" + IMPORT_FILES + "')")
    public ImportResponse importFiles(MultipartFile file) {

        byte[] bytes;
        try {
            bytes = file.getBytes();
        } catch (IOException e) {
            throw new BadRequestException("File could not be read and is likely corrupted.", e);
        }
        return fileExchangeImportService.importFileExchangeArchive(KeycloakSecurity.getUserId(), bytes);
    }


    @Override
    @PreAuthorize("hasAuthority('" + IMPORT_FILES + "')")
    public ImportResponse importDataset(MultipartFile file) {

        byte[] bytes;
        try {
            bytes = file.getBytes();
        } catch (IOException e) {
            throw new BadRequestException("File could not be read and is likely corrupted.", e);
        }
        return datasetExchangeService.importDataset(KeycloakSecurity.getUserId(), bytes);
    }

}
