package vn.azteam.tpf.service.impl;

import org.apache.commons.io.FilenameUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import vn.azteam.tpf.service.FileStorageService;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.Arrays;
import java.util.UUID;
import java.util.stream.Collectors;

@Service
public class FileStorageServiceImpl implements FileStorageService {
    private final Logger log = LoggerFactory.getLogger(FileStorageService.class);

    private final Path fileStorageLocation;

    @Autowired
    public FileStorageServiceImpl(
        @Value("#{'${app.upload-images}'?:systemProperties['java.io.tmpdir']}") String uploadPath) {
        this.fileStorageLocation = Paths.get(uploadPath)
            .toAbsolutePath().normalize();

        try {
            Files.createDirectories(this.fileStorageLocation);
        } catch (IOException e) {
            log.error("Could not create the directory where the uploaded files will be stored.", e);
        }
    }

    /**
     * Load file as resource
     * @param fileName
     * @return
     */
    @Override
    public Resource loadFileAsResource(String itemTimeLineId, String fileName) {
        try {
            Path filePath = this.fileStorageLocation.resolve(itemTimeLineId).resolve(fileName).normalize();
            Resource resource = new UrlResource(filePath.toUri());
            if(resource.exists()) {
                return resource;
            } else {
                throw new RuntimeException("File not found " + fileName);
            }
        } catch (MalformedURLException ex) {
            throw new RuntimeException("File not found " + fileName, ex);
        }
    }

    /**
     * Store upload files
     * @param itemTimeLineId
     * @param files
     * @return
     */
    @Override
    public String storeFiles(String itemTimeLineId, MultipartFile[] files){
        return Arrays.asList(files)
            .stream()
            .map(file -> storeFile(itemTimeLineId, file))
            .collect(Collectors.joining( ";" ));
    }

    /**
     * Remove file
     * @param itemTimeLineId
     * @param files
     * @return
     */
    @Override
    public Boolean removeFiles(String itemTimeLineId, String[] files){
        boolean blnRtn = true;
        for(String file : files){
            if(removeFile(itemTimeLineId, file) == false){
                blnRtn = false;
            }
        }
        return blnRtn;
    }

    /**
     * Store file
     * @param file
     * @return
     */
    private String storeFile(String itemTimeLineId, MultipartFile file) {
        // Normalize file name
        String fileName = StringUtils.cleanPath(file.getOriginalFilename());
        String fileExt = FilenameUtils.getExtension(fileName);

        try {
            // Check if the file's name contains invalid characters
            if(fileName.contains("..")) {
                throw new RuntimeException("Sorry! Filename contains invalid path sequence " + fileName);
            }

            // Renaming uploaded file with unique value.
            String fileId = String.valueOf(UUID.randomUUID());
            fileName = fileId+ FilenameUtils.EXTENSION_SEPARATOR + fileExt;

            Path targetDir = this.fileStorageLocation.resolve(itemTimeLineId);
            if(Files.notExists(targetDir)){
                Files.createDirectories(targetDir);
            }

            // Copy file to the target location (Replacing existing file with the same name)
            Path targetLocation = this.fileStorageLocation.resolve(itemTimeLineId).resolve(fileName);
            Files.copy(file.getInputStream(), targetLocation, StandardCopyOption.REPLACE_EXISTING);

            return itemTimeLineId + "/" + fileName;
        } catch (IOException e) {
            throw new RuntimeException("Could not store file " + fileName + ". Please try again!", e);
        }
    }

    /**
     * Remove file
     * @param fileName
     * @return
     */
    @Override
    public Boolean removeFile(String itemTimeLineId, String fileName) {
        // Remove file to the target location
        Path targetLocation = this.fileStorageLocation.resolve(itemTimeLineId).resolve(fileName);
        File file= new File(targetLocation.toString());
        if(file.exists()) {
            if(file.delete()){
                log.info("File deleted successfully: " + fileName );
                return true;
            }else{
                log.debug("Fail to delete file: " + fileName);
                return false;
            }
        }
        return false;
    }
}
