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

import java.io.File;
import java.io.FileInputStream;
import java.nio.file.Path;
import java.time.OffsetDateTime;
import java.util.List;

import org.springframework.dao.DataAccessException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.iqser.red.service.persistence.management.v1.processor.entity.ComponentMappingEntity;
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.model.ComponentMappingDownloadModel;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.ComponentMappingRepository;
import com.iqser.red.storage.commons.service.StorageService;
import com.knecon.fforesight.tenantcommons.TenantContext;

import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.experimental.FieldDefaults;

@Service
@Transactional
@RequiredArgsConstructor
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
public class ComponentMappingPersistenceService {

    StorageService storageService;

    ComponentMappingRepository repository;


    public List<ComponentMappingEntity> getByDossierTemplateId(String dossierTemplateId) {

        return repository.findByDossierTemplateId(dossierTemplateId);
    }


    public void deleteById(String dossierTemplateId, String id) {

        ComponentMappingEntity entity = getEntityById(dossierTemplateId, id);
        delete(entity);
    }


    public ComponentMappingEntity getEntityById(String dossierTemplateId, String id) {

        return repository.findByIdAndDossierTemplateId(id, dossierTemplateId)
                .orElseThrow(() -> new NotFoundException("ComponentMapping with id " + id + " not found!"));
    }


    private void delete(ComponentMappingEntity entity) {

        repository.delete(entity);
        storageService.deleteObject(TenantContext.getTenantId(), entity.getStorageId());
    }


    public void deleteByDossierTemplateId(String dossierTemplateId) {

        repository.findByDossierTemplateId(dossierTemplateId)
                .forEach(this::delete);
    }


    @SneakyThrows
    public void updateOrCreate(String storageId, File mappingFile, ComponentMappingEntity entity) {

        entity.setChangedDate(OffsetDateTime.now());
        try {
            repository.saveAndFlush(entity);
        } catch (DataAccessException ex) {
            throw new BadRequestException("Failed to save component mapping metadata due to invalid data.", ex);
        }
        try (var in = new FileInputStream(mappingFile)) {
            storageService.storeObject(TenantContext.getTenantId(), storageId, in);
        }
    }


    public boolean existsByNameAndDossierTemplateId(String name, String dossierTemplateId) {

        return repository.existsByNameAndDossierTemplateId(name, dossierTemplateId);
    }


    @SneakyThrows
    public ComponentMappingDownloadModel getMappingFileForDownload(String dossierTemplateId, String componentMappingId) {

        var entity = getEntityById(dossierTemplateId, componentMappingId);
        if (!storageService.objectExists(TenantContext.getTenantId(), entity.getStorageId())) {
            throw new NotFoundException("ComponentMapping with id " + componentMappingId + " does not exist!");
        }
        return new ComponentMappingDownloadModel(storageService.getObject(TenantContext.getTenantId(), entity.getStorageId()), entity.getEncoding(), entity.getFileName());
    }


    public File downloadMappingFileToFolder(String storageId, String fileName, Path outputDir) {

        File outputFile = outputDir.resolve(fileName).toFile();
        storageService.downloadTo(TenantContext.getTenantId(), storageId, outputFile);
        return outputFile;
    }


    public void setVersion(String componentMappingId, Integer version) {

        repository.updateVersion(componentMappingId, version);
    }

}
