package dev.fyloz.trial.colorrecipesexplorer.service.files;

import dev.fyloz.trial.colorrecipesexplorer.config.Preferences;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Service;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;

@Service
public class FilesService {

    private ResourceLoader resources;

    @Autowired
    public FilesService(ResourceLoader resources) {
        this.resources = resources;
    }

    /**
     * Lit un fichier dans le dossier resources.
     *
     * @param path Le chemin vers la fichier (sans classpath:, depuis le dossier resources)
     * @return Le contenu du fichier
     */
    public String readResource(String path) {
        String fullPath = String.format("classpath:%s", path);
        try (InputStream stream = resources.getResource(fullPath).getInputStream()) {
            return readInputStreamAsString(stream);
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * Lit un InputStream et le transforme en String
     *
     * @param input L'InputStream
     * @return Le InputStream transformé en String
     * @throws IOException La transformation vers un String a échoué
     */
    public String readInputStreamAsString(InputStream input) throws IOException {
        byte[] data = FileCopyUtils.copyToByteArray(input);
        return new String(data, StandardCharsets.UTF_8);
    }

    /**
     * Récupère le contenu d'un fichier dans un tableau de Byte.
     *
     * @param path Le chemin vers le fichier
     * @return Le contenu du fichier dans un tableau de Byte
     * @throws IOException La lecture du fichier a échoué
     */
    public byte[] readAsBytes(String path) throws IOException {
        return Files.readAllBytes(Paths.get(path));
    }

    /**
     * Récupère le contenu d'un fichier dans une liste de String.
     *
     * @param path Le chemin vers le fichier
     * @return Le contenu du fichier dans une liste de String
     * @throws IOException La lecture du fichier a échoué
     */
    public List<String> readAsStrings(String path) throws IOException {
        return Files.readAllLines(Paths.get(path));
    }

    /**
     * Écrit un fichier Multipart sur le disque.
     *
     * @param multipartFile Le fichier à écrire
     * @param path          Le chemin vers le fichier
     * @return Si le fichier a bien été créé
     */
    public boolean write(MultipartFile multipartFile, String path) {
        if (multipartFile.getSize() <= 0) return true;

        try {
            File file = create(path);
            multipartFile.transferTo(file.toPath());
            return true;
        } catch (IOException ex) {
            Preferences.logger.error("Impossible d'écrire un fichier Multipart: " + ex.getMessage());
            return false;
        }
    }

    /**
     * Crée un fichier sur le disque.
     *
     * @param path Le chemin vers le fichier
     * @return Le fichier créé
     * @throws IOException La création du fichier échoue
     */
    public File create(String path) throws IOException {
        File file = getFile(path);

        try {
            if (!file.exists() || file.isDirectory()) {
                Files.createDirectories(file.getParentFile().toPath());
                Files.createFile(file.toPath());
            }

            return file;
        } catch (IOException ex) {
            throw new RuntimeException("Impossible de créer un fichier: " + ex.getMessage());
        }
    }

    /**
     * Supprime un fichier sur le disque.
     *
     * @param path Le chemin vers le fichier
     */
    public void delete(String path) {
        File file = getFile(path);

        try {
            if (file.exists() && !file.isDirectory()) Files.delete(file.toPath());
        } catch (IOException ex) {
            throw new RuntimeException("Impossible de supprimer un fichier: " + ex.getMessage());
        }
    }

    /**
     * Vérifie si un fichier existe sur le disque.
     *
     * @param path Le chemin vers le fichier
     * @return Si le fichier existe
     */
    public boolean exists(String path) {
        File file = getFile(path);

        return file.exists() && !file.isDirectory();
    }

    public File getFile(String path) {
        return new File(path);
    }

    public String getPath(String fileName) {
        return String.format("%s/%s", Preferences.uploadDirectory, fileName);
    }

}
