package com.cusc.nirvana.user.rnr.fp.service.impl;

import cn.hutool.core.codec.Base64Decoder;
import cn.hutool.core.codec.Base64Encoder;
import com.alibaba.excel.util.IoUtils;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.*;
import com.cusc.nirvana.common.result.Response;
import com.cusc.nirvana.user.exception.CuscUserException;
import com.cusc.nirvana.user.rnr.fp.common.ResponseCode;
import com.cusc.nirvana.user.rnr.fp.config.ChineseFontComponent;
import com.cusc.nirvana.user.rnr.fp.dto.FileDownloadDTO;
import com.cusc.nirvana.user.rnr.fp.dto.FileRecordDTO;
import com.cusc.nirvana.user.rnr.fp.service.IFileService;
import com.cusc.nirvana.user.rnr.fp.util.RnrFpRestTemplateUtils;
import com.cusc.nirvana.user.util.CuscStringUtils;
import com.cusc.nirvana.user.util.DateUtils;
import com.cusc.nirvana.user.util.crypt.DataCryptService;
import com.cusc.nirvana.user.util.crypt.Sm4Util;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.im4java.core.CompositeCmd;
import org.im4java.core.IMOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RequestCallback;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.font.FontRenderContext;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.util.Arrays;
import java.util.Date;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * @author stayAnd
 * @date 2022/6/22
 */
@Service
@Slf4j
public class OssFileServiceImpl implements IFileService {

	/**
	 * GraphicsMagick 安装目录
	 */
	public static final String PATH_GRAPHICS_MAGICK = "/data/GraphicsMagick/bin";

	@Value("${oss.endpoint}")
	private String endpoint;


	@Value("${oss.accessKeyId}")
	private String accessKeyId;


	@Value("${oss.accessKeySecret}")
	private String accessKeySecret;


	@Value("${oss.bucketName}")
	private String bucketName;

	@Value("${rnr.image.watermark.position:8}")
	public int watermarkPosition;
	@Value("${rnr.image.watermark.fontcolor:#DC143C}")
	public String watermarkFontColor;
	@Value("${rnr.image.watermark.alpha:50}")
	public int watermarkAlpha;
	@Value("${rnr.image.watermark.fontsize:15}")
	public int watermarkFontSize;
	@Value("${rnr.image.watermark.fonttype:HeiTi}")
	public String watermarkFontType;

	@Value("${rnr.image.watermark.content:仅限用于实名业务}")
	private String watermarkContent;

	@Value("${rnr.image.watermark.source:/data/imageTemp/source/}")
	private String sourceBasePath;
	@Value("${rnr.image.watermark.water:/data/imageTemp/water/}")
	private String waterBasePath;
	@Value("${rnr.image.watermark.target:/data/imageTemp/target/}")
	private String targetBasePath;
	@Resource
	private ChineseFontComponent chineseFontComponent;
	@Resource
	private ExecutorService textWaterMarkThreadPool4createWaterMark;

	@Autowired
	private DataCryptService dataCryptService;

	@Value("${T1.picturePower}")
	private boolean picturePower;

	@Override
	public String upLoadImage(InputStream inputStream,String suffix) {
		return upLoadFile(inputStream,suffix);
	}

	@Override
	public String upLoadFile(InputStream inputStream,String suffix) {
		// 创建OSSClient实例。
		OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);

		String ossFileKey = CuscStringUtils.generateUuid()+"."+suffix;
		PutObjectRequest request = new PutObjectRequest(bucketName,ossFileKey,inputStream);
		ossClient.putObject(request);
		ossClient.shutdown();

		return ossFileKey;
	}

	//方案:IM4JAVA+GraphicsMagick实现中文水印
	@Override
	public String doMarkText(MultipartFile reqFile, String userId) throws Exception {
		String fontType = watermarkFontType;//字体
		String colorStr = watermarkFontColor; //颜色
		int alpha = watermarkAlpha;
		int fontSize = watermarkFontSize;
		String filePre = "[" + UUID.randomUUID().toString().replace("-", "") + "]";
		String fileName = filePre + reqFile.getOriginalFilename();

//       1） file-->本地存储到sourcePath
		File localImageFile = new File(new File(sourceBasePath).getAbsolutePath() + "/" + fileName);
		if (!localImageFile.getParentFile().exists()) {
			localImageFile.getParentFile().mkdirs();
		}
		reqFile.transferTo(localImageFile);

		// 将文字生成图片，再合成
		// 1、生成透明的背景图片
		File watermarkImage = new File(new File(waterBasePath).getAbsolutePath() + "/" + fileName);
		String watermarkContent = getWatermarkContent(userId);
		boolean res = textWaterMarkThreadPool4createWaterMark.submit(() -> this.convertFontToImage(watermarkContent, fontType, fontSize, colorStr, watermarkImage.getAbsolutePath())).get();
		if (!res) {
			throw new CuscUserException("500", "文字水印生成临时文件异常");
		}

		// 2、合成图片
		String waterPath = watermarkImage.getAbsolutePath();
		String sourceImagePath = sourceBasePath + fileName;
		String targetImagePath = targetBasePath + fileName;
		int[] watermarkImgSide = getImageSide(watermarkImage.getAbsolutePath());
		int[] srcImgSide = getImageSide(sourceImagePath);
		int[] xy = getXY(srcImgSide, watermarkImgSide, watermarkPosition, 0, 0);
		CompositeCmd cmd = new CompositeCmd(true);
		cmd.setSearchPath(PATH_GRAPHICS_MAGICK);
		IMOperation op = new IMOperation();
		op.dissolve(alpha);
		op.geometry(watermarkImgSide[0], watermarkImgSide[1], xy[0], xy[1]);
		op.addImage(waterPath);
		op.addImage(sourceImagePath);
		op.addImage(targetImagePath);
		cmd.run(op);

		return fileName;
	}
	@Override
	public String upLoadImgBase64(String base64, String suffix) {
		try {
			byte[] bytes = Base64Decoder.decode(base64);
			ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
			return upLoadImage(inputStream,suffix);
		} catch (Exception e) {
			log.error("upLoadImgBase64 has error");
		}
		return "";
	}

	@Override
	public String upLoadFileBase64(String base64, String suffix) {
		try {
			byte[] bytes = Base64Decoder.decode(base64);
			ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
			return upLoadFile(inputStream,suffix);
		} catch (Exception e) {
			log.error("upLoadImgBase64 has error");
		}
		return "";
	}

	@Override
	public String getFileUrl(String fileKey) {
		// 创建OSSClient实例。
		OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
		Date date = new Date(System.currentTimeMillis() +(3600L * 1000) );
		URL url = ossClient.generatePresignedUrl(bucketName,fileKey,date);
		log.info("OssFileServiceImpl getFileUrl fileKey = {},url = {}",fileKey,url.toString());
		ossClient.shutdown();
		return url.toString();
	}

	@Override
	public String downLoadBase64(String fileKey) {
		// 创建OSSClient实例。
		OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
		try {
			GetObjectRequest request = new GetObjectRequest(bucketName,fileKey);
			OSSObject object = ossClient.getObject(request);
			InputStream inputStream = object.getObjectContent();

			byte[] bytes = IoUtils.toByteArray(inputStream);

			byte[] imageContent = new byte[1024];


			//解密失败，原文返回
			try {
				imageContent = Sm4Util.decryptEcbPaddingByte(dataCryptService.getSm4Key().getBytes(), bytes);
			} catch (Exception e) {
				return Base64Encoder.encode(bytes);
			}
			//解密成功 解密返回
			return Base64Encoder.encode(picturePower?imageContent:bytes);
		} catch (Exception e) {
			e.printStackTrace();
			throw new CuscUserException(ResponseCode.SYSTEM_ERROR.getCode(),"文件oss下载失败");
		} finally {
			ossClient.shutdown();
		}
	}

	@Override
	public Double getFileSize(String fileKey) {
		OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
		boolean existFlag = ossClient.doesObjectExist(bucketName, fileKey);
		if(!existFlag){
			throw new CuscUserException("500","文件不存在");
		}
		ObjectMetadata objectMetadata = ossClient.getObjectMetadata(bucketName, fileKey);
		Double fileSize = (double) objectMetadata.getContentLength()/1024;
		ossClient.shutdown();
		return fileSize;
	}

	@Override
	public byte[] downLoadBytes(String fileKey) {

		// 创建OSSClient实例。
		OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
		GetObjectRequest request = new GetObjectRequest(bucketName,fileKey);
		OSSObject object = ossClient.getObject(request);
		InputStream inputStream = object.getObjectContent();

		try {
			byte[] bytes = IoUtils.toByteArray(inputStream);
			ossClient.shutdown();
			return bytes;
		} catch (Exception e){
			throw new CuscUserException(ResponseCode.SYSTEM_ERROR.getCode(),"文件oss bytes下载失败");
		}
	}

	/**---------------------------------------私有化方法-----------------------------------------------------------------------*/

	private String getWatermarkContent(String userId) {
		StringBuilder rv = new StringBuilder(watermarkContent);
		rv.append("[");
		rv.append(StringUtils.isBlank(userId) ? "" : userId);
		rv.append("]");
		rv.append("[");
		rv.append(DateUtils.currentTimeStr());
		rv.append("]");
		return rv.toString();
	}


	private int[] getXY(int[] image, int[] watermark, int position, int x, int y) {
		int[] xy = new int[2];
		if (position == 1) {
			xy[0] = x;
			xy[1] = y;
		} else if (position == 2) {
			xy[0] = (image[0] - watermark[0]) / 2;            //横向边距
			xy[1] = y;  //纵向边距
		} else if (position == 3) {
			xy[0] = image[0] - watermark[0] - x;
			xy[1] = y;
		} else if (position == 4) {
			xy[0] = x;
			xy[1] = (image[1] - watermark[1]) / 2;
		} else if (position == 5) {
			xy[0] = (image[0] - watermark[0]) / 2;
			xy[1] = (image[1] - watermark[1]) / 2;
		} else if (position == 6) {
			xy[0] = image[0] - watermark[0] - x;
			xy[1] = (image[1] - watermark[1]) / 2;
		} else if (position == 7) {
			xy[0] = x;
			xy[1] = image[1] - watermark[1] - y;
		} else if (position == 8) {
			xy[0] = (image[0] - watermark[0]) / 2;
			xy[1] = image[1] - watermark[1] - y;
		} else {
			xy[0] = image[0] - watermark[0] - x;
			xy[1] = image[1] - watermark[1] - y;
		}
		return xy;
	}

	private int[] getImageSide(String imgPath) throws IOException {
		int[] side = new int[2];
		Image img = ImageIO.read(new File(imgPath));
		side[0] = img.getWidth(null);
		side[1] = img.getHeight(null);
		return side;
	}

	/**
	 * 将文字转换为图片（解决中文字符乱码以及文字透明度问题）
	 *
	 * @param text      文本内容
	 * @param fontType  字体类型（HeiTi、KaiTi、SongTi）
	 * @param fontSize  字体大小
	 * @param fontColor 字体颜色
	 * @param outFile   输出文件路径
	 */
	private boolean convertFontToImage(String text, String fontType, Integer fontSize, String fontColor, String outFile) {
		long startTime = System.currentTimeMillis();
		Color textColor = Color.BLACK;
		if (StringUtils.isNotBlank(fontColor)) {
			textColor = new Color(Integer.valueOf(fontColor, 16));
		}
		// 获取字体库映射字体集
		Font font = chineseFontComponent.getChineseFont(fontType).deriveFont(Font.PLAIN, fontSize);
		File file = new File(outFile);
		// 获取font的样式应用在str上的整个矩形
		Rectangle2D r = font.getStringBounds(text, new FontRenderContext(AffineTransform.getScaleInstance(1, 1), false, false));
		// 获取单个字符的高度
		int unitHeight = (int) Math.floor(r.getHeight());
		// 获取整个str用了font样式的宽度这里用四舍五入后+1保证宽度绝对能容纳这个字符串作为图片的宽度
		int width = (int) Math.round(r.getWidth()) + 1;
		// 把单个字符的高度+3保证高度绝对能容纳字符串作为图片的高度
		int height = unitHeight + 3;
		// 创建图片
		BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
		Graphics2D g2d = image.createGraphics();
		image = g2d.getDeviceConfiguration().createCompatibleImage(width, height, Transparency.TRANSLUCENT);
		g2d.dispose();
		g2d = image.createGraphics();
		g2d.setStroke(new BasicStroke(1));
		g2d.setColor(textColor); // 在换成所需要的字体颜色
		g2d.setFont(font);
		g2d.drawString(text, 0, font.getSize());
		try {
			ImageIO.write(image, "png", file); // 输出png图片
		} catch (IOException e) {
			log.error("文字水印生成临时文件出现异常", e);
			return false;
		}
		log.info("生成临时文件时间：{} ms", (System.currentTimeMillis() - startTime));
		return true;
	}

}
