package com.oqpo.api.service;

import com.oqpo.api.constant.ApiConstants;
import com.oqpo.api.entity.oper.FileEntity;
import com.oqpo.api.enums.SystemMessageCode;
import com.oqpo.api.exception.GlobalException;
import com.oqpo.api.mapper.FileMapper;
import com.oqpo.api.model.FileModel;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPReply;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.text.DecimalFormat;
import java.util.List;
import java.util.UUID;

@Slf4j
@Service
public class FileService extends CommonService {
    @Autowired
    private FileMapper fileMapper;

    @Value("${ftp.path}")
    String ftpPath;

    @Value("${ftp.host}")
    String ftpHost;

    @Value("${ftp.port}")
    Integer ftpPort;

    @Value("${ftp.user}")
    String ftpUser;

    @Value("${ftp.pass}")
    String ftpPass;

    @Value("${ftp.domain}")
    String ftpDomain;

    @Value("${file.domain.url}")
    String domainUrl;

    @Value("${file.upload.path}")
    String uploadPath;

    @Value("${file.upload.folder}")
    String uploadFolder;

    public FileEntity fileUpload(String userId, String fileNo, String path, MultipartFile multipartFile) throws Exception {
        FileModel fileModel = new FileModel();
        FTPClient client = new FTPClient();

        try {
            if ((ApiConstants.FILE_MAX * (1024*1024*100)) < multipartFile.getSize()) {
                throw new GlobalException(SystemMessageCode.FILE_CAPACITY_FAIL);
            }

            client.setControlEncoding("UTF-8");
            client.connect(ftpHost, ftpPort);

            int resultCode = client.getReplyCode();
            if (!FTPReply.isPositiveCompletion(resultCode)) {
                System.out.println("FTP server refused connection.!");
                return null;
            } else {
                client.setSoTimeout(1000);
                if (!client.login(ftpUser, ftpPass)) {
                    System.out.println("Login Error!");
                    return null;
                }
            }

            String orgFileName = multipartFile.getOriginalFilename();
            String extension = FilenameUtils.getExtension(orgFileName);
            String fileName = UUID.randomUUID().toString() + "." + extension;
            String savePath = uploadPath + "/" + fileName;
            String remotePath = ftpPath + uploadFolder + "/" + path + "/" + fileName;
            String downUrl =  ftpDomain + uploadFolder + "/" + path + "/" + fileName;

            File file = new File(savePath);
            file.getParentFile().mkdirs();
            multipartFile.transferTo(file);

            FileInputStream inputStream = new FileInputStream(file);
            client.enterLocalPassiveMode();
            client.makeDirectory(ftpPath + uploadFolder + "/" + path);
            client.setFileType(FTP.BINARY_FILE_TYPE);
            client.storeFile(remotePath, inputStream);
            inputStream.close();

            deleteFolder(uploadPath);

            fileModel.setFileNo(fileNo);
            fileModel.setFileName(fileName);
            fileModel.setFileOrgName(orgFileName);
            fileModel.setDomainUrl(downUrl);
            fileModel.setFilePath(path);
            fileModel.setExtension(extension);
            fileModel.setFileSize(byteCalculation(multipartFile.getSize()));
            return saveFile(userId, fileModel);

        } catch (GlobalException e) {
            throw new GlobalException(e.getSystemMessageCode());
        } catch (Exception e) {
            e.printStackTrace();
            throw new GlobalException(SystemMessageCode.FILE_ERR);
        } finally {
            try {
                if (client.isConnected()) {
                    client.logout();
                    client.disconnect();
                }
            } catch (Throwable e) {
                e.printStackTrace();
            }
        }
    }

    public FileEntity saveFile(String userId, FileModel fileModel) {
        String fileNo = fileModel.getFileNo();
        if (fileNo.trim().equals("")) {
            fileNo = this.fnGetKeyNo(1);
        }
        String filePath = domainUrl + uploadFolder + "/" + fileModel.getFilePath() + "/" + fileModel.getFileName();
        FileEntity fileEntity = new FileEntity();
        fileEntity.setFileNo(fileNo);
        fileEntity.setFileOrgNm(fileModel.getFileOrgName());
        fileEntity.setFileNm(fileModel.getFileName());
        fileEntity.setFilePath(filePath);
        fileEntity.setFileType(fileModel.getFilePath());
        fileEntity.setFileSize(fileModel.getFileSize());
        fileEntity.setAddId(userId);
        fileMapper.insertFile(fileEntity);
        return fileEntity;
    }

    public List<FileEntity> fileList(String fileNo) throws Exception {
        return fileMapper.selectFileList(fileNo);
    }

    public void fileDelete(String fileNo, Integer fileSeq) {
        fileMapper.deleteFile(fileNo, fileSeq);
    }

    public void fileDownload(String fileNo, Integer fileSeq, HttpServletRequest request, HttpServletResponse response) throws Exception {
        FileModel fileModel = new FileModel();
        FTPClient client = new FTPClient();

        try {
            FileEntity entity = fileMapper.selectFile(fileNo, fileSeq);
            if (entity == null) {
                throw new GlobalException(SystemMessageCode.FILE_DOWN_ERR);
            }

            String filePath = ftpPath + uploadFolder + "/" + entity.getFileType() + "/";
            String fileName = entity.getFileNm();
            client.setControlEncoding("UTF-8");
            client.connect(ftpHost, ftpPort);

            int resultCode = client.getReplyCode();
            if (!FTPReply.isPositiveCompletion(resultCode)) {
                System.out.println("FTP server refused connection.!");
            } else {
                client.setSoTimeout(1000);
                if (!client.login(ftpUser, ftpPass)) {
                    System.out.println("Login Error!");
                }
            }

            File file = new File(uploadPath);
            file.mkdirs();

            FileOutputStream fo = new FileOutputStream(uploadPath + fileName);
            client.enterLocalPassiveMode();
            client.setFileType(FTP.BINARY_FILE_TYPE);

            if (client.retrieveFile(filePath + fileName, fo)) {
                System.out.println("Download - Success");
            }
            fo.close();
            fileModel.setFilePath(uploadPath + fileName);

            File attachFile = new File(fileModel.getFilePath());
            if (attachFile.exists() && attachFile.isFile()) {
                response.setContentType("application/octet-stream; charset=utf-8");
                response.setContentLength((int) attachFile.length());
                String browser = getBrowser(request);
                String disposition = getDisposition(entity.getFileOrgNm(), browser);
                response.setHeader("Content-Disposition", disposition);
                response.setHeader("Content-Transfer-Encoding", "binary");
                OutputStream out = response.getOutputStream();
                FileInputStream fis = null;
                fis = new FileInputStream(attachFile);
                FileCopyUtils.copy(fis, out);
                if (fis != null) fis.close();
                out.flush();
                out.close();
            } else {
                throw new GlobalException(SystemMessageCode.FILE_DOWN_ERR);
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new GlobalException(SystemMessageCode.FILE_DOWN_ERR);
        } finally {
            try {
                deleteFolder(uploadPath);
                if (client.isConnected()) {
                    client.logout();
                    client.disconnect();
                }
            } catch (Throwable e) {
                e.printStackTrace();
            }
        }
    }

    public void deleteFolder(String delPath) {
        File folder = new File(delPath);
        File[] folderList = folder.listFiles();

        for (int i = 0; i < folderList.length; i++) {
            if (folderList[i].isFile()) {
                folderList[i].delete();
            } else {
                deleteFolder(folderList[i].getPath());
            }
            folderList[i].delete();
        }
        folder.delete();
    }

    public String byteCalculation(Long bytes) {
        String retFormat = "0";
        String[] s = { "bytes", "KB", "MB", "GB", "TB", "PB" };

        if (bytes != 0) {
            int idx = (int) Math.floor(Math.log(bytes) / Math.log(1024));
            DecimalFormat df = new DecimalFormat("#,###.##");
            double ret = ((bytes / Math.pow(1024, Math.floor(idx))));
            retFormat = df.format(ret) + " " + s[idx];
        } else {
            retFormat += " " + s[0];
        }

        return retFormat;
    }

    public String getBrowser(HttpServletRequest request) {
        String header = request.getHeader("User-Agent");
        if (header.indexOf("MSIE") > -1 || header.indexOf("Trident") > -1)
            return "MSIE";
        else if (header.indexOf("Chrome") > -1)
            return "Chrome";
        else if (header.indexOf("Opera") > -1)
            return "Opera";
        return "Firefox";
    }

    public String getDisposition(String filename, String browser) throws UnsupportedEncodingException {
        String dispositionPrefix = "attachment;filename=";
        String encodedFilename = null;
        if (browser.equals("MSIE")) {
            encodedFilename = URLEncoder.encode(filename, "UTF-8").replaceAll("\\+", "%20");
        } else if (browser.equals("Firefox")) {
            encodedFilename = "\"" + new String(filename.getBytes("UTF-8"), "8859_1") + "\"";
        } else if (browser.equals("Opera")) {
            encodedFilename = "\"" + new String(filename.getBytes("UTF-8"), "8859_1") + "\"";
        } else if (browser.equals("Chrome")) {
            StringBuffer sb = new StringBuffer();
            for (int i = 0; i < filename.length(); i++) {
                char c = filename.charAt(i);
                if (c > '~') {
                    sb.append(URLEncoder.encode("" + c, "UTF-8"));
                } else {
                    sb.append(c);
                }
            }
            encodedFilename = sb.toString();
        }
        return dispositionPrefix + encodedFilename;
    }
}
