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

import java.io.BufferedInputStream;
import java.io.File;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;

import org.apache.commons.io.IOUtils;
import org.springframework.stereotype.Service;

import com.iqser.red.service.persistence.management.v1.processor.exception.InternalServerErrorException;
import com.iqser.red.service.persistence.management.v1.processor.exception.NotFoundException;
import com.iqser.red.service.persistence.management.v1.processor.utils.StorageIdUtils;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentLog;
import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.EntityLog;
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.RedactionLog;
import com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlog.imported.ImportedRedactions;
import com.iqser.red.service.persistence.service.v1.api.shared.model.redactionlog.section.SectionGrid;
import com.iqser.red.storage.commons.exception.StorageException;
import com.iqser.red.storage.commons.exception.StorageObjectDoesNotExist;
import com.iqser.red.storage.commons.service.StorageService;
import com.knecon.fforesight.tenantcommons.TenantContext;

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

@Slf4j
@Service
@RequiredArgsConstructor
public class FileManagementStorageService {

    private final StorageService storageService;


    @SneakyThrows
    public byte[] getStoredObjectBytes(String dossierId, String fileId, FileType fileType) {

        InputStream inputStream = getObject(TenantContext.getTenantId(), StorageIdUtils.getStorageId(dossierId, fileId, fileType));
        byte[] storedObjectBytes = IOUtils.toByteArray(inputStream);
        inputStream.close();
        return storedObjectBytes;
    }


    @SneakyThrows
    public byte[] getStoredObjectBytes(String storageId) {

        InputStream inputStream = getObject(TenantContext.getTenantId(), storageId);
        byte[] storedObjectBytes = IOUtils.toByteArray(inputStream);
        inputStream.close();
        return storedObjectBytes;
    }


    @SneakyThrows
    public InputStream getObject(String tenantId, String storageId) {

        File tempFile = File.createTempFile("temp", ".data");
        storageService.downloadTo(tenantId, storageId, tempFile);
        return new BufferedInputStream(Files.newInputStream(Paths.get(tempFile.getPath()), StandardOpenOption.DELETE_ON_CLOSE));
    }


    public void storeObject(String dossierId, String fileId, FileType fileType, InputStream stream) {

        storageService.storeObject(TenantContext.getTenantId(), StorageIdUtils.getStorageId(dossierId, fileId, fileType), stream);
    }


    public <T> void storeJSONObject(String dossierId, String fileId, FileType fileType, T jsonObject) {

        storageService.storeJSONObject(TenantContext.getTenantId(), StorageIdUtils.getStorageId(dossierId, fileId, fileType), jsonObject);
    }


    public void storeObject(String storageId, InputStream data) {

        storageService.storeObject(TenantContext.getTenantId(), storageId, data);
    }


    public RedactionLog getRedactionLog(String dossierId, String fileId) {

        try {
            return storageService.readJSONObject(TenantContext.getTenantId(), StorageIdUtils.getStorageId(dossierId, fileId, FileType.REDACTION_LOG), RedactionLog.class);
        } catch (StorageObjectDoesNotExist e) {
            log.debug("RedactionLog does not exist");
            throw new NotFoundException(String.format("RedactionLog does not exist for Dossier ID \"%s\" and File ID \"%s\"!", dossierId, fileId));
        } catch (StorageException e) {
            throw new InternalServerErrorException(e.getMessage());
        }
    }


    public EntityLog getEntityLog(String dossierId, String fileId) {

        try {
            return storageService.readJSONObject(TenantContext.getTenantId(), StorageIdUtils.getStorageId(dossierId, fileId, FileType.ENTITY_LOG), EntityLog.class);
        } catch (StorageObjectDoesNotExist e) {
            log.debug("EntityLog does not exist");
            throw new NotFoundException(String.format("EntityLog does not exist for Dossier ID \"%s\" and File ID \"%s\"!", dossierId, fileId));
        } catch (StorageException e) {
            throw new InternalServerErrorException(e.getMessage());
        }
    }


    public void storeRedactionLog(String dossierId, String fileId, RedactionLog redactionLog) {

        storageService.storeJSONObject(TenantContext.getTenantId(), StorageIdUtils.getStorageId(dossierId, fileId, FileType.REDACTION_LOG), redactionLog);

    }


    public SectionGrid getSectionGrid(String dossierId, String fileId) {

        try {
            return storageService.readJSONObject(TenantContext.getTenantId(), StorageIdUtils.getStorageId(dossierId, fileId, FileType.SECTION_GRID), SectionGrid.class);
        } catch (StorageObjectDoesNotExist e) {
            log.debug("SectionGrid not available.");
            throw new NotFoundException("SectionGrid does not exist");
        } catch (Exception e) {
            throw new RuntimeException("Could not convert SectionGrid", e);
        }
    }


    public ImportedRedactions getImportedRedactions(String dossierId, String fileId) {

        try {
            return storageService.readJSONObject(TenantContext.getTenantId(),
                    StorageIdUtils.getStorageId(dossierId, fileId, FileType.IMPORTED_REDACTIONS),
                    ImportedRedactions.class);
        } catch (StorageObjectDoesNotExist e) {
            throw new NotFoundException("ImportedRedactions does not exist");
        } catch (Exception e) {
            throw new RuntimeException("Could not convert ImportedRedactions", e);
        }
    }


    public boolean objectExists(String dossierId, String fileId, FileType origin) {

        return storageService.objectExists(TenantContext.getTenantId(), StorageIdUtils.getStorageId(dossierId, fileId, origin));
    }


    public boolean objectExists(String storageId) {

        return storageService.objectExists(TenantContext.getTenantId(), storageId);
    }


    public void deleteObject(String dossierId, String fileId, FileType fileType) {

        storageService.deleteObject(TenantContext.getTenantId(), StorageIdUtils.getStorageId(dossierId, fileId, fileType));
    }


    public void deleteObject(String storageId) {

        storageService.deleteObject(TenantContext.getTenantId(), storageId);
    }


    public ComponentLog getComponentLog(String dossierId, String fileId) {

        try {
            return storageService.readJSONObject(TenantContext.getTenantId(), StorageIdUtils.getStorageId(dossierId, fileId, FileType.COMPONENT_LOG), ComponentLog.class);
        } catch (StorageObjectDoesNotExist e) {
            log.debug("ComponentLog does not exist");
            throw new NotFoundException(String.format("ComponentLog does not exist for Dossier ID \"%s\" and File ID \"%s\"!", dossierId, fileId));
        } catch (StorageException e) {
            throw new InternalServerErrorException(e.getMessage());
        }
    }

}
