package com.chinaweal.aiccs.common.util.filestorage.adapter;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.TimeInterval;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.ftp.Ftp;
import cn.hutool.extra.ftp.FtpConfig;
import cn.hutool.extra.ftp.FtpMode;
import com.chinaweal.aiccs.common.util.filestorage.FileStorageService;
import com.chinaweal.youfool.framework.springboot.exception.custom.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

/**
 * FTP文件存储下载服务实现.
 *
 * @author yangmc
 * @date 2021-11-06
 * @time 13:31
 */
@Component
@ConditionalOnProperty(prefix = "storage", name = "mode", havingValue = "ftp")
@Slf4j
public class FtpStorageAdapter implements FileStorageService {

    @Value("${storage.ftp.ip}")
    private String IP;

    @Value("${storage.ftp.port}")
    private int PORT;

    @Value("${storage.ftp.username}")
    private String USERNAME;

    @Value("${storage.ftp.password}")
    private String PASSWORD;


    /**
     * 初始化FTP链接
     *
     * @return
     */
    public Ftp init() {
        FtpConfig ftpConfig = new FtpConfig(IP, PORT, USERNAME, PASSWORD, CharsetUtil.CHARSET_UTF_8);
        ftpConfig.setConnectionTimeout(30000);
        ftpConfig.setSoTimeout(30000);
        return new Ftp(ftpConfig, FtpMode.Passive);
    }


    /**
     * 上传文件到指定目录，可选：
     *
     * <pre>
     * 1. path为null或""上传到当前路径
     * 2. path为相对路径则相对于当前路径的子路径
     * 3. path为绝对路径则上传到此路径
     * </pre>
     *
     * @param destPath 服务端路径，可以为{@code null} 或者相对路径或绝对路径
     * @param file     文件
     * @return 是否上传成功
     */
    @Override
    public boolean writeFromFile(String destPath, File file) {
        try (Ftp ftp = init()) {
            return ftp.upload(destPath, file.getName(), file);
        } catch (IOException e) {
            log.error("上传FTP出错!", e);
            return false;
        }
    }

    /**
     * 上传文件到指定目录
     *
     * @param path       服务端文件路径路径
     * @param fileStream 文件流
     * @return 是否上传成功
     */
    @Override
    public boolean writeFromStream(String path, InputStream fileStream) {
        TimeInterval timer = DateUtil.timer();
        final String fileName = FileUtil.getName(path);
        final String dir = StrUtil.removeSuffix(path, fileName);
        try (Ftp ftp = init()) {
            boolean upload = ftp.upload(dir, fileName, fileStream);
            if(!upload){
                throw new RuntimeException("上传FTP出错!");
            }else {
                return true;
            }
        } catch (IOException e) {
            log.error("上传FTP出错!", e);
            throw new RuntimeException("上传FTP出错!");
        } finally {
            IoUtil.close(fileStream);
            log.debug("上传FTP耗时(毫秒): {}", timer.interval());
        }

    }

    /**
     * 上传文件到指定目录
     *
     * @param path       服务端路径，可以为{@code null} 或者相对路径或绝对路径
     * @param fileName   文件名
     * @param fileStream 文件流
     * @return 是否上传成功
     */
    @Override
    public boolean writeFromStream(String path, String fileName, InputStream fileStream) {
        TimeInterval timer = DateUtil.timer();
        try (Ftp ftp = init()) {
            boolean upload = ftp.upload(path, fileName, fileStream);
            if(!upload){
                throw new RuntimeException("上传FTP出错!");
            }else {
                return true;
            }
        } catch (IOException e) {
            log.error("上传FTP出错!", e);
            throw new RuntimeException("上传FTP出错!");
        } finally {
            IoUtil.close(fileStream);
            log.debug("上传FTP耗时(毫秒): {}", timer.interval());
        }
    }


    /**
     * 下载文件
     *
     * @param path     文件所在路径（远程目录），不包含文件名
     * @param fileName 文件名
     * @param outFile  输出文件或目录，当为目录时使用服务端文件名
     */
    @Override
    public void writeToFile(String path, String fileName, File outFile) {
        try (Ftp ftp = init()) {
            ftp.download(path, fileName, outFile);
        } catch (IOException e) {
            log.error("下载FTP出错!", e);
        }
    }

    /**
     * 下载文件
     *
     * @param path    文件所在路径（远程目录），包含文件名
     * @param outFile 输出文件或目录，当为目录时使用服务端文件名
     */
    @Override
    public void writeToFile(String path, File outFile) {
        final String fileName = FileUtil.getName(path);
        final String dir = StrUtil.removeSuffix(path, fileName);
        try (Ftp ftp = init()) {
            ftp.download(dir, fileName, outFile);
        } catch (IOException e) {
            log.error("下载FTP出错!", e);
        }
    }

    /**
     * 下载文件到输出流
     *
     * @param path 文件路径（含文件名）
     * @param out  输出位置
     */
    @Override
    public void writeToStream(String path, OutputStream out) {
        final String fileName = FileUtil.getName(path);
        final String dir = StrUtil.removeSuffix(path, fileName);
        try (Ftp ftp = init()) {
            ftp.download(dir, fileName, out);
        } catch (IOException e) {
            log.error("下载FTP出错!", e);
        }
    }

    /**
     * 下载文件到输出流
     *
     * @param path     文件路径
     * @param fileName 文件名
     * @param out      输出位置
     */
    @Override
    public void writeToStream(String path, String fileName, OutputStream out) {
        try (Ftp ftp = init()) {
            ftp.download(path, fileName, out);
        } catch (IOException e) {
            log.error("下载FTP出错!", e);
        }
    }

    @Override
    public boolean delFile(String path) {
        try (Ftp ftp = init()) {
            return ftp.delFile(path);
        } catch (IOException e) {
            log.error("删除FTP文件出错!", e);
            return false;
        }
    }

    @Override
    public boolean delDir(String dirPath) {
        try (Ftp ftp = init()) {
            return ftp.delDir(dirPath);
        } catch (IOException e) {
            log.error("删除FTP文件夹出错!", e);
            return false;
        }
    }

    /**
     * 获取路径下的文件名称数组
     * 这里path后面不带斜杠 /
     * @param path
     * @return
     */
    public List<String> getFtpFileNames(String path) {
        List<String> filenames = new ArrayList<>();
        try (Ftp ftp = init()) {
            // 路径不存在，返回空数组
            if (ftp.exist(path)) {
                filenames = ftp.ls(path);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return filenames;
    }
}
