package com.tofly.feesapi.file.fileconfiguration;

import cn.hutool.core.util.StrUtil;
import com.tofly.feesapi.common.exception.FeesErrorCode;
import com.tofly.feesapi.common.exception.FeesException;
import com.tofly.feesapi.file.entity.dto.FileUploadBase64Dto;
import com.tofly.feesapi.file.entity.dto.FileUploadResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.net.ftp.FTPClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotNull;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Base64;
import java.util.Objects;


@Slf4j
@Service
public class FTPFileStorageService implements FileStorageService {

    @Autowired
    private FTPFileStorageProperties fileStorageProperties;

    // 储存文件
    public String storeFile(MultipartFile file, String path) {
        String fileName = StringUtils.cleanPath(Objects.requireNonNull(file.getOriginalFilename()));
        try {
            return storeFileByInputStream(path, fileName, file.getContentType(), file.getInputStream());
        } catch (Exception ex) {
            log.error("FTPFileStorageService.storeFile() 失败 =========> ", ex);
            throw new FeesException("当前新增对象中,文件上传失败,请重试", FeesErrorCode.BUSINESS_ERROR);
        }
    }

    public String storeFileBase64(String path, String originalFilename, String contentType, InputStream inputStream) {
        String fileName = StringUtils.cleanPath(Objects.requireNonNull(originalFilename));
        try {
            return storeFileByInputStream(path, fileName, contentType, inputStream);
        } catch (Exception ex) {
            log.error("FTPFileStorageService.storeFileBase64() 失败 =========> ", ex);
            throw new FeesException("当前新增对象中,文件上传失败,请重试", FeesErrorCode.BUSINESS_ERROR);
        }
    }

    @Override
    public FileUploadResponse upload(MultipartFile file, String path) {
        String fileName = storeFile(file, path);
        String staticPath = getPath(fileName);
        String realDownloadPath = null;
        try {
            // 获取request
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            // 生成下载接口的地址
            realDownloadPath = request.getRequestURL().toString().replace("upload", "downloadFile/" + fileName);
        } catch (Exception e) {
            log.error("FTPFileStorageService.upload() 失败 =========> ", e);
        }
        return new FileUploadResponse(fileName, realDownloadPath, file.getContentType(), file.getSize(), staticPath);
    }

    public FileUploadResponse uploadFileBase64(FileUploadBase64Dto file, String Path) {
        String fileBase64 = file.getBase64String();
        if (fileBase64.contains(",")) {
            fileBase64 = fileBase64.split(",")[1];
        }

        byte[] bytes = Base64.getDecoder().decode(fileBase64);
        InputStream inputStream = new ByteArrayInputStream(bytes);
        long fileSize = bytes.length;
        String fileName = this.storeFileBase64(Path, file.getOriginalFilename(), file.getContentType(), inputStream);
        String staticPath = getPath(fileName);
        String realDownloadPath = null;
        try {
            HttpServletRequest request =
                    ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            realDownloadPath = request.getRequestURL().toString().replace("upload", "downloadFile/" + fileName);
        } catch (Exception e) {
            log.error("FTPFileStorageService.uploadFileBase64() 失败 =========> ", e);
        }
        return new FileUploadResponse(fileName, realDownloadPath, file.getContentType(), fileSize, staticPath);
    }

    @Override
    public ResponseEntity<?> download(
            String fileName, HttpServletRequest request, HttpServletResponse response) {
        FTPClient ftpClient = null;

        try {
            ftpClient = FTPClientPoolManager.borrowClient();
            // 获取文件流
            InputStream inputStream = ftpClient.retrieveFileStream(fileName);
            // 获取输出流
            OutputStream outputStream = response.getOutputStream();
            // 清空
            response.reset();
            // 写入流
            byte[] buffer = readAllBytes(inputStream);
            outputStream.write(buffer);
            outputStream.flush();
            inputStream.close();
            // 配置header
            response.addHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + fileName);
            response.addHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(buffer.length));
            return ResponseEntity.status(HttpStatus.OK).body("download...");
        } catch (Exception e) {
            String err = fileName + "file not found";
            log.error(err);
            return ResponseEntity.status(HttpStatus.NOT_FOUND).body(err);
        }
    }

    @Override
    public byte[] getByteDataforFile(String fileName) {
        FTPClient ftpClient = null;
        try {
            ftpClient = FTPClientPoolManager.borrowClient();
            // 获取文件流
            InputStream inputStream = ftpClient.retrieveFileStream(fileName);

            byte[] buffer = readAllBytes(inputStream);
            inputStream.close();
            return buffer;
        } catch (Exception e) {
            log.error(e.getMessage());
            return null;
        } finally {

        }
    }

    public InputStream getByteDataforFileInputStream(String fileName) {
        FTPClient ftpClient = null;
        try {
            ftpClient = FTPClientPoolManager.borrowClient();

            InputStream inputStream = ftpClient.retrieveFileStream(fileName);
            // 写入流
            return inputStream;
        } catch (Exception e) {
            log.error(e.getMessage());
            return null;
        }
    }

    @Override
    public Boolean retrieveFile(String remotePath, OutputStream outputStream) {
        FTPClient ftpClient = null;
        try {
            ftpClient = FTPClientPoolManager.borrowClient();
            InputStream inputStream = ftpClient.retrieveFileStream(remotePath);
            if (IOUtils.copy(inputStream, outputStream) > -1) {
                return true;
            }
            return false;
        } catch (Exception e) {
            log.error(e.getMessage());
            return false;
        }
    }

    @Override
    public String uploadFile(String Path, String fileName, String contentType, InputStream inputStream)
            throws IOException {
        try {
            return getPath(storeFileByInputStream(Path, fileName, contentType, inputStream));
        } catch (Exception e) {
            throw new IOException(e.getMessage());
        }
    }

    /**
     * 删除文件
     */
    @Override
    public void removeFile(String PathfileName) throws IOException {
        FTPClient ftpClient = null;
        try {
            ftpClient = FTPClientPoolManager.borrowClient();
            PathfileName = new String(PathfileName.getBytes("GBK"), StandardCharsets.ISO_8859_1);
            ftpClient.deleteFile(PathfileName);
        } catch (Exception e) {
            throw new IOException(e.getMessage());
        }
    }

    @NotNull
    private String getPath(String fileName) {
        return fileStorageProperties
                .getServer()
                .concat(":")
                .concat(String.valueOf(fileStorageProperties.getPort()))
                .concat("/")
                .concat(fileName);
    }

    private String storeFileByInputStream(String path, String fileName, String contentType, InputStream inputStream) throws IOException {
        if (!fileName.contains(".")) {
            fileName = RandomStringUtils.randomAlphabetic(15);
        } else {
            fileName = RandomStringUtils.randomAlphabetic(15) + fileName.substring(fileName.lastIndexOf("."));
        }

        if (fileName.contains("..")) {
        }

        path += "/" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMM"));
        FTPClient ftpClient = null;
        try {
            ftpClient = FTPClientPoolManager.borrowClient();
            //切换到上传目录
            if (!ftpClient.changeWorkingDirectory(path)) {
                //如果目录不存在创建目录
                String[] dirs = path.split("/");
                String tempPath = "";

                for (String dir : dirs) {
                    if (StrUtil.isEmpty(dir)) {
                        continue;
                    }
                    tempPath += "/" + dir;
                    if (!ftpClient.changeWorkingDirectory(tempPath)) {
                        if (!ftpClient.makeDirectory(tempPath)) {
                            throw new FeesException("FTP创建文件夹失败", FeesErrorCode.BUSINESS_ERROR);
                        } else {
                            ftpClient.changeWorkingDirectory(tempPath);
                        }
                    }
                }
            }

            if (!ftpClient.storeFile(fileName, inputStream)) {
                throw new FeesException("上传文件失败", FeesErrorCode.BUSINESS_ERROR);
            }
            return path + "/" + fileName;
        } catch (Exception e) {
            log.error(e.getMessage());
            return null;
        }
    }


    private static byte[] readAllBytes(InputStream inputStream) throws IOException {
        byte[] buffer = new byte[1024];
        int bytesRead;
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        while ((bytesRead = inputStream.read(buffer)) != -1) {
            outputStream.write(buffer, 0, bytesRead);
        }
        return outputStream.toByteArray();
    }
}
