package com.artfess.file.attachmentService;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

import com.artfess.file.model.FileStorage;
import com.artfess.file.params.FlowUploadPropertiesStorageDTO;
import com.artfess.file.persistence.manager.FileStorageManager;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPClientConfig;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import org.springframework.stereotype.Service;

import com.artfess.base.attachment.Attachment;
import com.artfess.base.attachment.AttachmentService;
import com.artfess.base.util.AppUtil;
import com.artfess.base.util.BeanUtils;
import com.artfess.base.util.StringUtil;
import com.artfess.file.model.FlowUploadProperties;
import com.artfess.file.config.FtpEntity;
import com.artfess.file.persistence.manager.FlowUploadPropertiesManager;
import com.artfess.file.util.AppFileUtil;

@Service
public class FtpAttachmentServiceImpl implements AttachmentService{


	//连接FTP服务器
	private void connect(FtpEntity ftpEntity,FTPClient ftp){
		try {
			int reply;
			ftp.connect(ftpEntity.getUrl(), ftpEntity.getPort());
			FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_NT);
			conf.setServerLanguageCode("zh");
			boolean loginResult = ftp.login(ftpEntity.getUsername(), ftpEntity.getPassword());
			if(loginResult){
				// 开启服务器对UTF-8的支持，如果服务器支持就用UTF-8编码，否则就使用本地编码（GBK）.
				if (FTPReply.isPositiveCompletion(ftp.sendCommand("OPTS UTF8", "ON"))) {
					ftpEntity.setLOCAL_CHARSET("UTF-8");
				}
				ftp.setControlEncoding(ftpEntity.getLOCAL_CHARSET());
				ftp.enterLocalPassiveMode(); // 设置被动模式
			}
			reply = ftp.getReplyCode();
			if (!FTPReply.isPositiveCompletion(reply)) {
				ftp.disconnect();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@Override
	public void upload(Attachment attachment, InputStream inputStream,String propertiesId) throws Exception {
		FtpEntity ftpEntity = AppUtil.getBean(FtpEntity.class);
		FTPClient ftp = new FTPClient();
		//如果是流程附件，则获取其上传配置信息
		ftpEntity = initUploadProperties(propertiesId,attachment,ftpEntity,true);
		String path = attachment.getFilePath();
		String fileName = attachment.getEntryptName()?attachment.getId():attachment.getFileName() + "." + attachment.getExtensionName();
		path = replaceFileSeparator(path);
		path = new String(path.getBytes(ftpEntity.getLOCAL_CHARSET()), ftpEntity.getSERVER_CHARSET());
		//fileName = new String(fileName.getBytes(LOCAL_CHARSET), SERVER_CHARSET);
		validConnection(ftpEntity,ftp);
		// 转到指定上传目录,没有该目录则创建
		CreateDirecroty(path,ftp);
		ftp.setFileType(FTP.BINARY_FILE_TYPE);
		boolean result = ftp.storeFile(fileName, inputStream);
		if (!result) {
			throw new RuntimeException("上传文件失败");
		}
		// 关闭输入流
		inputStream.close();
	}

	@Override
	public void download(Attachment attachment, OutputStream outStream,String propertiesId) throws Exception{
		//如果是流程附件，则获取其上传配置信息
		FtpEntity ftpEntity = AppUtil.getBean(FtpEntity.class);
		FTPClient ftp = new FTPClient();
		ftpEntity = initUploadProperties(propertiesId,attachment,ftpEntity,false);
		validConnection(ftpEntity,ftp);
		String path = attachment.getFilePath();
		path = replaceFileSeparator(path);
		String fileName =  attachment.getEntryptName()?attachment.getId():attachment.getFileName() + "." + attachment.getExtensionName();
		// 编码转换，避免上传到服务器上时出现中文乱码
		path = getFtpPath(path);
		path = new String(path.getBytes(ftpEntity.getLOCAL_CHARSET()), ftpEntity.getSERVER_CHARSET());
		boolean changeResult = ftp.changeWorkingDirectory(path);// 转移到FTP服务器目录
		if(!changeResult){
			throw new RuntimeException("要下载的文件路径不存在");
		}
		FTPFile[] fs = ftp.listFiles();
		boolean tag = false;
		for (FTPFile ff : fs) {
			String n = ff.getName();
			if (n.equals(fileName)) {
				tag = true;
//				fileName = new String(fileName.getBytes(LOCAL_CHARSET), SERVER_CHARSET);
				ftp.retrieveFile(fileName, outStream);
				outStream.close();
				break;
			}
		}
		if(!tag){
			throw new RuntimeException("要下载的文件不存在");
		}
	}

	@Override
	public boolean chekckFile(Attachment attachment,String propertiesId) throws Exception{
		//如果是流程附件，则获取其上传配置信息
		FtpEntity ftpEntity =AppUtil.getBean(FtpEntity.class);
		FTPClient ftp = new FTPClient();
		ftpEntity = initUploadProperties(propertiesId,attachment,ftpEntity,false);
		boolean ref=true;
		validConnection(ftpEntity,ftp);
		String path = attachment.getFilePath();
		path = replaceFileSeparator(path);
		String fileName = attachment.getEntryptName()?attachment.getId():attachment.getFileName() + "." + attachment.getExtensionName();
		// 编码转换，避免上传到服务器上时出现中文乱码
		path = getFtpPath(path);
		path = new String(path.getBytes(ftpEntity.getLOCAL_CHARSET()), ftpEntity.getSERVER_CHARSET());
		boolean changeResult = ftp.changeWorkingDirectory(path);// 转移到FTP服务器目录
		if(!changeResult){
			ref=false;
		}
		FTPFile[] fs = ftp.listFiles();
		boolean tag = false;
		for (FTPFile ff : fs) {
			String n = ff.getName();
			if (n.equals(fileName)) {
				tag = true;
				break;
			}
		}
		if(!tag){
			ref=false;
		}
		return  ref;
	}

	@Override
	public void remove(Attachment attachment,String propertiesId) throws Exception {
		//如果是流程附件，则获取其上传配置信息
		FtpEntity ftpEntity =AppUtil.getBean(FtpEntity.class);
		FTPClient ftp = new FTPClient();
		ftpEntity = initUploadProperties(propertiesId,attachment,ftpEntity,false);
		validConnection(ftpEntity,ftp);
		String path = attachment.getFilePath();
		path = replaceFileSeparator(path);
		String fileName =  attachment.getEntryptName()?attachment.getId():attachment.getFileName()+ "." + attachment.getExtensionName();
		ftp.deleteFile(path + ftpEntity.getSeparator() + fileName);
	}

	//验证连接
	private void validConnection(FtpEntity ftpEntity,FTPClient ftp){
		try{
			if(!ftp.isConnected()||!ftp.isRemoteVerificationEnabled()||!ftp.sendNoOp()){
				connect(ftpEntity,ftp);
			}
			// 重置当前目录到根目录
			ftp.changeWorkingDirectory(File.separator);
		}
		catch(Exception e){
			connect(ftpEntity,ftp);
		}
	}

	/*//转移FTP文件目录位置(不存在的目录会自动创建)
	private void changeDirectory(String path) throws Exception{
		boolean changeResult = ftp.changeWorkingDirectory(path);
		//转移失败，则该目录不存在
		if(!changeResult){
			String errorMsg = "上传文件时，创建文件路径失败";
			//创建目录
			int mkd = ftp.mkd(path);
			if(mkd!=257){
				throw new RuntimeException(errorMsg);
			}
			//转移到该目录
			changeResult = ftp.changeWorkingDirectory(path);
			if(!changeResult){
				throw new RuntimeException(errorMsg);
			}
		}
	}*/


	/**
	 * 创建多层目录文件，如果有ftp服务器已存在该文件，则不创建，如果无，则创建
	 * @param remote
	 * @return
	 * @throws IOException
	 */
	private  boolean CreateDirecroty(String remote,FTPClient ftp) throws IOException {
		boolean success = true;
		remote = getFtpPath(remote);
		String separator = "/";
		String directory = remote + separator;
		// 如果远程目录不存在，则递归创建远程服务器目录
		if (!directory.equalsIgnoreCase(separator) && !changeWorkingDirectory(new String(directory),ftp)) {
			int start = 0;
			int end = 0;
			if (directory.startsWith(separator)) {
				start = 1;
			} else {
				start = 0;
			}
			end = directory.indexOf(separator, start);
			String path = "";
			String paths = "";
			while (true) {
				String subDirectory = new String(remote.substring(start, end).getBytes("GBK"), "iso-8859-1");
				path = path + separator + subDirectory;
				if (!existFile(path,ftp)) {
					if (makeDirectory(subDirectory,ftp)) {
						changeWorkingDirectory(subDirectory,ftp);
					} else {
						System.out.println("创建目录[" + subDirectory + "]失败");
						changeWorkingDirectory(subDirectory,ftp);
					}
				} else {
					changeWorkingDirectory(subDirectory,ftp);
				}

				paths = paths + separator + subDirectory;
				start = end + 1;
				end = directory.indexOf(separator, start);
				// 检查所有目录是否创建完毕
				if (end <= start) {
					break;
				}
			}
		}
		return success;
	}

	/**
	 * 改变目录路径
	 * @param directory
	 * @return
	 */
	private  boolean changeWorkingDirectory(String directory,FTPClient ftp) {
		boolean flag = true;
		try {
			flag = ftp.changeWorkingDirectory(directory);
			if (flag) {
				System.out.println("进入文件夹" + directory + " 成功！");

			} else {
				System.out.println("进入文件夹" + directory + " 失败！开始创建文件夹");
			}
		} catch (IOException ioe) {
			ioe.printStackTrace();
		}
		return flag;
	}

	/**
	 * 判断ftp服务器文件是否存在
	 * @param path
	 * @return
	 * @throws IOException
	 */
	private  boolean existFile(String path,FTPClient ftp) throws IOException {
		boolean flag = false;
		FTPFile[] ftpFileArr = ftp.listFiles(path);
		if (ftpFileArr.length > 0) {
			flag = true;
		}
		return flag;
	}



	/**
	 * 创建目录
	 * @param dir
	 * @return
	 */
	private boolean makeDirectory(String dir,FTPClient ftp) {
		boolean flag = true;
		try {
			flag = ftp.makeDirectory(dir);
			if (flag) {
				System.out.println("创建文件夹" + dir + " 成功！");

			} else {
				System.out.println("创建文件夹" + dir + " 失败！");
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return flag;
	}






	private String replaceFileSeparator(String path){
		// 替换路径中的分隔符
		String ftpFormatPath = regReplace(path, File.separator);
		// 替换文件名，只保留路径
		ftpFormatPath = ftpFormatPath.replaceAll("/\\w+\\.?(\\w+)?(\\s+)?$", "");
		if(path.equals(ftpFormatPath)){
			ftpFormatPath = getFtpPath(ftpFormatPath);
			ftpFormatPath = ftpFormatPath.replaceAll("/\\w+\\.?(\\w+)?(\\s+)?$", "");
			ftpFormatPath = ftpFormatPath.replaceAll("/", "\\\\");
		}
		return ftpFormatPath;
	}

	private String regReplace(String str, String replaceChar){
		StringBuffer resultString = new StringBuffer();
		try {
			Pattern regex = Pattern.compile("[\\|/]");
			Matcher regexMatcher = regex.matcher(str);
			while (regexMatcher.find()) {
				regexMatcher.appendReplacement(resultString, replaceChar);
			}
			regexMatcher.appendTail(resultString);
		} catch (PatternSyntaxException ex) {
			ex.printStackTrace();
		}
		return resultString.toString();
	}

	@Override
	public String getStoreType() {
		return "ftp";
	}

	/**
	 * 根据配置id获取文件上传配置
	 * @param propertiesId
	 * @return
	 */
	private FtpEntity initUploadProperties(String propertiesId,Attachment defaultFile,FtpEntity ftpEntity,boolean isUpload){
		if(StringUtil.isNotEmpty(propertiesId)){
			FileStorageManager fileStorageManager = AppUtil.getBean(FileStorageManager.class);
			FileStorage fileStorage = fileStorageManager.get(propertiesId);
			if(BeanUtils.isNotEmpty(fileStorage)){
				String location = fileStorage.getLocation();
				if(isUpload && StringUtil.isNotEmpty(location)){
					location = location.replace("/", "\\");
					defaultFile.setFilePath(location+defaultFile.getFilePath());
				}
				FtpEntity ftpEntity2 =new FtpEntity();
				ftpEntity2.setUrl(fileStorage.getUrl());
				ftpEntity2.setPort(fileStorage.getPort());
				ftpEntity2.setUsername(fileStorage.getUserName());
				ftpEntity2.setPassword(fileStorage.getPassword());
				defaultFile.setEntryptName(fileStorage.getEncryptName()==0?false:true);
				return ftpEntity2;
			}else if(isUpload){
				String sysPath = AppFileUtil.getAttachPath();
				if(StringUtil.isNotEmpty(sysPath)){
					sysPath = sysPath.replace("/", "\\");
					defaultFile.setFilePath(sysPath+defaultFile.getFilePath());
				}
			}else{
				FlowUploadPropertiesManager uploadPropertiesManager = AppUtil.getBean(FlowUploadPropertiesManager.class);
				FlowUploadPropertiesStorageDTO uploadProperties = uploadPropertiesManager.getById(propertiesId);
				if(BeanUtils.isNotEmpty(uploadProperties)){
					String location = uploadProperties.getLocation();
					if(isUpload && StringUtil.isNotEmpty(location)){
						location = location.replace("/", "\\");
						defaultFile.setFilePath(location+defaultFile.getFilePath());
					}
					FtpEntity ftpEntity2 =new FtpEntity();
					ftpEntity2.setUrl(uploadProperties.getUrl());
					ftpEntity2.setPort(uploadProperties.getPort());
					ftpEntity2.setUsername(uploadProperties.getUserName());
					ftpEntity2.setPassword(uploadProperties.getPassword());
					defaultFile.setEntryptName(uploadProperties.getEncryptName()==0?false:true);
					return ftpEntity2;
				}
			}
			return ftpEntity;
		}
		return ftpEntity;
	}

	@Override
	public byte[] getFileBytes(Attachment sysFile) throws Exception {
		return null;
	}

	@Override
	public String getUrl(Attachment attachment) {
		return null;
	}

	@Override
	public String initMultiPartUpload(Attachment attachment) {
		return null;
	}

	@Override
	public String getChunkUrl(Attachment attachment, Integer partNumber, String uploadId) {
		return null;
	}

	@Override
	public boolean mergeMultipartUpload(Attachment attachment, String uploadId, int realyPartNumber) {
		return false;
	}

	private String getFtpPath(String path){
		if(StringUtil.isNotEmpty(path)){
			path = path.replaceAll("\\\\", "/");
		}
		return path;
	}
}
