package com.iqser.red.service.peristence.v1.server.integration.utils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.apache.commons.io.IOUtils;
import org.springframework.core.io.InputStreamResource;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.iqser.red.commons.jackson.ObjectMapperFactory;
import com.iqser.red.storage.commons.exception.StorageObjectDoesNotExist;
import com.iqser.red.storage.commons.service.StorageService;

import lombok.SneakyThrows;

public class FileSystemBackedStorageService implements StorageService {

    private final Map<String, File> dataMap = new HashMap<>();


    public FileSystemBackedStorageService() {

    }


    @Override
    @SneakyThrows
    public <T> void storeJSONObject(String tenantId, String objectId, T any) {

        File tempFile = File.createTempFile("test", ".tmp");
        getMapper().writeValue(new FileOutputStream(tempFile), any);
        dataMap.put(objectId, tempFile);
    }


    private ObjectMapper getMapper() {

        return ObjectMapperFactory.create();
    }


    @Override
    @SneakyThrows
    public <T> T readJSONObject(String tenantId, String objectId, Class<T> clazz) {

        if (dataMap.get(objectId) == null || !dataMap.get(objectId).exists()) {
            throw new StorageObjectDoesNotExist("Stored object not found");
        }
        return getMapper().readValue(new FileInputStream(dataMap.get(objectId)), clazz);
    }



    @SneakyThrows
    public void storeObject(String tenantId, String objectId, InputStream stream) {

        File tempFile = File.createTempFile("test", ".tmp");

        try (var fileOutputStream = new FileOutputStream(tempFile)) {
            IOUtils.copy(stream, fileOutputStream);
        }

        dataMap.put(objectId, tempFile);
    }


    @SneakyThrows
    @Override
    public void downloadTo(String tenantId, String objectId, File destinationFile) {

        var res = dataMap.get(objectId);
        if (res == null) {
            throw new StorageObjectDoesNotExist(new RuntimeException());
        }
        IOUtils.copy(new FileInputStream(res), new FileOutputStream(destinationFile));
    }


    @Override
    public void deleteObject(String tenantId, String objectId) {

        dataMap.remove(objectId);
    }


    @Override
    public boolean objectExists(String tenantId, String objectId) {

        return dataMap.containsKey(objectId);
    }


    public List<String> listPaths() {

        return new ArrayList<>(dataMap.keySet());
    }


    public List<String> listFilePaths() {

        return dataMap.values().stream().map(File::getAbsolutePath).collect(Collectors.toList());
    }


    public void clearStorage() {

        this.dataMap.forEach((k, v) -> {
            v.delete();
        });
        this.dataMap.clear();
    }

    @SneakyThrows
    @Override
    public InputStreamResource getObject(String tenantId, String objectId) {

        var res = dataMap.get(objectId);
        if (res == null) {
            throw new StorageObjectDoesNotExist(new RuntimeException());
        }
        return new InputStreamResource(new FileInputStream(res));

    }

}
