package com.iqser.red.service.persistence.management.v1.processor.service;

import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.UPLOAD_FILE;
import static com.iqser.red.service.persistence.management.v1.processor.roles.ActionRoles.WRITE_FILE_ATTRIBUTES;

import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.Map;

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;

import com.google.common.collect.Lists;
import com.google.common.hash.HashFunction;
import com.google.common.hash.Hashing;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.AuditPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.utils.StorageIdUtils;
import com.iqser.red.service.persistence.service.v1.api.shared.model.AuditCategory;
import com.iqser.red.service.persistence.service.v1.api.shared.model.FileUploadResult;
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.ImportCsvRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.ImportCsvResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.AddFileRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileType;
import com.iqser.red.storage.commons.service.StorageService;
import com.knecon.fforesight.keycloakcommons.security.KeycloakSecurity;
import com.knecon.fforesight.tenantcommons.TenantContext;

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

@Slf4j
@Service
@RequiredArgsConstructor
public class UploadService {

    private final HashFunction hashFunction = Hashing.murmur3_128();
    private final FileService fileService;
    private final DossierManagementService dossierManagementService;
    private final AuditPersistenceService auditPersistenceService;
    private final FileAttributesManagementService fileAttributesManagementService;
    private final StorageService storageService;


    @PreAuthorize("hasAuthority('" + WRITE_FILE_ATTRIBUTES + "')")
    public FileUploadResult importCsv(String dossierId, byte[] fileContent) {

        ImportCsvResponse response = fileAttributesManagementService.importCsv(dossierId, new ImportCsvRequest(fileContent));
        auditPersistenceService.audit(AuditRequest.builder()
                .userId(KeycloakSecurity.getUserId())
                .objectId(dossierId)
                .category(AuditCategory.DOSSIER.name())
                .message("File attributes csv processed.")
                .build());

        return FileUploadResult.builder()
                .processedAttributes(Lists.newArrayList(response.getAffectedFileIds()))
                .processedFileIds(Lists.newArrayList(response.getAffectedFileIds()))
                .build();
    }


    @PreAuthorize("hasAuthority('" + UPLOAD_FILE + "')")
    public FileUploadResult processSingleFile(String dossierId, String fileName, byte[] fileContent, boolean keepManualRedactions) {

        dossierManagementService.getDossierById(dossierId, false, false);

        var fileId = generateFileId(fileName, dossierId);
        var storageId = StorageIdUtils.getStorageId(dossierId, fileId, FileType.UNTOUCHED);

        try {

            storageService.storeObject(TenantContext.getTenantId(), storageId, new ByteArrayInputStream(fileContent));
            fileService.upload(new AddFileRequest(fileName, fileId, dossierId, KeycloakSecurity.getUserId()), keepManualRedactions, fileContent.length);

        } catch (Exception e) {
            storageService.deleteObject(TenantContext.getTenantId(), storageId);
            throw e;
        }

        auditPersistenceService.audit(AuditRequest.builder()
                .userId(KeycloakSecurity.getUserId())
                .objectId(dossierId)
                .category(AuditCategory.DOSSIER.name())
                .message("File has been uploaded.")
                .details(Map.of("fileId", fileId))
                .build());

        return FileUploadResult.builder().fileIds(Collections.singletonList(fileId)).build();

    }


    private String generateFileId(String fileName, String dossierId) {

        return hashFunction.hashBytes((fileName + dossierId).getBytes(StandardCharsets.UTF_8)).toString();
    }

}
