package com.ruoyi.system.service;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.img.Img;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.HexUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
import cn.hutool.crypto.symmetric.SymmetricCrypto;
import com.ruoyi.common.core.domain.PageQuery;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.helper.DataPermissionHelper;
import com.ruoyi.common.utils.IdUtils;
import com.ruoyi.system.domain.SysOss;
import com.ruoyi.system.domain.bo.SysOssBo;
import com.ruoyi.system.domain.vo.SysOssVo;
import org.slf4j.LoggerFactory;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.function.Supplier;

/**
 * 文件上传 服务层
 *
 * @author Lion Li
 */
public interface ISysOssService {

  /**
   * 服务商
   */
  public static enum Service {
    minio, qiniu, aliyun, qcloud, image, upload;
  }

  String IMAGE_WEBP = "webp";

  /**
   * 默认路径前缀
   */
  String PRE_DEFAULT = "default";

  /**
   * 设置指定服务商
   *
   * @param handle
   */
  void setService(Service service, Runnable handle);

  public <T> T setService(Service service, Supplier<T> handle);

  /**
   * 只保存，不存放记录
   *
   * @param handle 处理执行方法
   */
  void ignore(Runnable handle);


  /**
   * 只保存，不存放记录
   *
   * @param handle 处理执行方法
   */
  public <T> T ignore(Supplier<T> handle);


  TableDataInfo<SysOssVo> queryPageList(SysOssBo sysOss, PageQuery pageQuery);

  List<SysOssVo> listByIds(Collection<Long> ossIds);

  SysOssVo getById(Long ossId);

  default SysOssVo upload(MultipartFile file) {
    return upload(file, PRE_DEFAULT);
  }

  /**
   * @param file
   * @param pre  - 图片保存路径前缀
   * @return
   */
  default SysOssVo upload(MultipartFile file, String pre) {
    if (file == null || file.isEmpty()) {
      throw new RuntimeException("文件不能为空");
    }
    try {
      return save(file.getInputStream(), file.getOriginalFilename(), file.getContentType(), pre);
    } catch (IOException e) {
      throw new RuntimeException(e.getMessage(), e);
    }
  }


  /**
   * 保存文件, rule:{yyyy}/{MM}/{dd}/{id36}.{ext}
   *
   * @param in          输入流
   * @param filename    文件名
   * @param contentType 文件类型
   * @param pre         路径前缀
   * @return
   */
  default SysOssVo save(InputStream in, String filename, String contentType, String pre) {
    return save(in, filename, contentType, pre, "{yyyy}/{MM}/{dd}/{id36}.{ext}");
  }

  /**
   * 保存文件，保留文件名,rule:{yyyy}/{MM}/{dd}/{id36}/{filename}.{ext}
   *
   * @param in          输入流
   * @param filename    文件名
   * @param contentType 文件类型
   * @param pre         路径前缀
   * @return
   */
  default SysOssVo saveFilename(InputStream in, String filename, String contentType, String pre) {
    return save(in, filename, contentType, pre, "{yyyy}/{MM}/{dd}/{id36}/{filename}.{ext}");
  }

  /**
   * 保存文件，存放路径规则rule：
   * <pre>
   * - 根据存放规则和文件名生成存放的URI
   * - 支持
   * 	- {yyyy}/{MM}/{dd}/{HH}/{mm}/{ss} 年月日时分秒
   * 	- {UUID} 32位的唯一标志
   * 	- {i} 自增id
   * 	- {id} 当日int类型的唯一id,防止重复建议+年月日路径
   * 	- {id16} 当日int类型的唯一id16进制表示,防止重复建议+年月日路径
   * 	- {id36} 当日int类型的唯一id36进制表示,防止重复建议+年月日路径
   * 	- {filename} 文件基础名称
   * 	- {ext} 扩展名
   * </pre>
   *
   * @param in          输入流
   * @param filename    文件名
   * @param contentType 文件类型
   * @param pre         路径前缀
   * @param rule        路径规则
   * @return
   */
  SysOssVo save(InputStream in, String filename, String contentType, String pre, String rule);

  SysOssVo uploadImgs(MultipartFile file, String pre);

  default SysOssVo uploadImgs(MultipartFile file, String pre, int maxWidth, int maxHeight, BufferedImage watermark) {
    if (file == null || file.isEmpty()) {
      throw new RuntimeException("图片不能为空");
    }
    try {
      return uploadImgs(file.getInputStream(), pre, maxWidth, maxHeight, watermark);
    } catch (IOException e) {
      throw new RuntimeException(e.getMessage(), e);
    }
  }

  /**
   * @param in
   * @param pre       - 图片保存路径前缀
   * @param maxWidth  - 缩放到指定的宽 小于1表示不缩放
   * @param maxHeight - 缩放到指定的高 小于1表示不缩放
   * @param watermark - 水印图片 null表示不加水印
   * @return
   */
  SysOssVo uploadImgs(InputStream in, String pre, int maxWidth, int maxHeight,
                      BufferedImage watermark);

  void download(Long ossId, HttpServletResponse response) throws IOException;

  void download(String url, Service service, HttpServletResponse response) throws IOException;

  InputStream download(Long ossId) throws IOException;

  InputStream download(SysOssVo sysOss) throws IOException;

  InputStream download(String url, Service service) throws IOException;

  Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);


  default InputStream formatImage(MultipartFile file, int maxWidth, int maxHeight,
                                  BufferedImage watermark) {
    try {
      if (file == null || file.isEmpty()) {
        throw new RuntimeException("上传图片不能为空");
      }
      if (!file.getContentType().startsWith("image/")) {
        throw new RuntimeException("上传的文件不是图片");
      }
      return formatImage(file.getInputStream(), maxWidth, maxHeight, watermark);
    } catch (IOException e) {
      throw new RuntimeException("处理图片错误", e);
    }
  }

  default InputStream formatImage(Image img, int maxWidth, int maxHeight, BufferedImage watermark) {
    if (img == null) {
      throw new RuntimeException("图片不能为空");
    }
    return formatImage(Img.from(img), maxWidth, maxHeight, watermark);
  }

  default InputStream formatImage(InputStream in, int maxWidth, int maxHeight, BufferedImage watermark) {
    return formatImage(Img.from(in), maxWidth, maxHeight, watermark);
  }

  default InputStream formatImage(Img img, int maxWidth, int maxHeight, BufferedImage watermark) {
    try {
      int w = img.getImg().getWidth(null);
      int h = img.getImg().getHeight(null);
      if (maxWidth > 0 && maxHeight > 0) {
        if (w > maxWidth || h > maxHeight) {
          int outWidth = 0;
          int outHeight = 0;
          outHeight = maxWidth * h / w;
          if (outHeight > maxHeight) {
            outHeight = maxHeight;
            outWidth = outHeight * w / h;
          } else {
            outWidth = maxWidth;
          }
          img = img.scale(outWidth, outHeight);
          w = outWidth;
          h = outHeight;

        }
      }


      if (watermark != null) {
        int ww = watermark.getWidth(null);
        int wh = watermark.getHeight(null);
        if (w > ww && h > wh) {
          img = img.pressImage(watermark, 0, 0, 1f);
        }
      }

      ByteArrayOutputStream pout = new ByteArrayOutputStream();

      img.setTargetImageType(IMAGE_WEBP).write(pout);

      ByteArrayInputStream pin = new ByteArrayInputStream(pout.toByteArray());
      pout.close();
      pout = null;
      return pin;
    } catch (Exception e) {
      throw new RuntimeException("处理图片错误", e);
    }
  }


  /**
   * <pre>
   * - 根据存放规则和文件名生成存放的URI
   * - 支持
   * 	- {yyyy}/{MM}/{dd}/{HH}/{mm}/{ss} 年月日时分秒
   * 	- {UUID} 32位的唯一标志
   *  - {i} 自增id
   * 	- {id} 当日int类型的唯一id,防止重复建议+年月日路径
   * 	- {id16} 当日int类型的唯一id16进制表示,防止重复建议+年月日路径
   * 	- {id36} 当日int类型的唯一id36进制表示,防止重复建议+年月日路径
   * 	- {filename} 文件基础名称
   * 	- {ext} 扩展名
   * </pre>
   *
   * @param rule
   * @param filename
   * @return
   */
  default String generateURI(String rule, String filename) {
    Calendar c = Calendar.getInstance();
    if (rule.contains("{yyyy}")) {
      rule = rule.replace("{yyyy}", "" + c.get(Calendar.YEAR));
    }
    if (rule.contains("{MM}")) {
      rule = rule.replace("{MM}", String.format("%02d", c.get(Calendar.MONTH) + 1));
    }
    if (rule.contains("{dd}")) {
      rule = rule.replace("{dd}", String.format("%02d", c.get(Calendar.DATE)));
    }
    if (rule.contains("{HH}")) {
      rule = rule.replace("{HH}", String.format("%02d", c.get(Calendar.HOUR_OF_DAY)));
    }
    if (rule.contains("{mm}")) {
      rule = rule.replace("{mm}", String.format("%02d", c.get(Calendar.MINUTE)));
    }
    if (rule.contains("{ss}")) {
      rule = rule.replace("{ss}", String.format("%02d", c.get(Calendar.SECOND)));
    }
    if (rule.contains("{UUID}")) {
      rule = rule.replace("{UUID}", UUID.fastUUID().toString(true));
    }
    if (rule.contains("{i}")) {
      rule = rule.replace("{i}", IdUtils.nextId(Id.groupName).toString());
    }
    if (rule.contains("{id}")) {
      rule = rule.replace("{id}", Long.toString(id.nextId()));
    }
    if (rule.contains("{id16}")) {
      rule = rule.replace("{id16}", Long.toString(id.nextId(), 16));
    }
    if (rule.contains("{id36}")) {
      rule = rule.replace("{id36}", Long.toString(id.nextId(), 36));
    }
    if (rule.contains("{filename}")) {
      String temp = null;
      if (filename.contains(".")) {
        temp = filename.substring(0, filename.lastIndexOf("."));
      } else {
        temp = filename;
      }
      rule = rule.replace("{filename}", temp);
    }

    if (rule.contains("{ext}")) {
      String temp = null;
      if (filename.contains(".")) {
        temp = filename.substring(filename.lastIndexOf(".") + 1);
      } else {
        temp = "";
      }
      rule = rule.replace("{ext}", temp.toLowerCase());
    }
    return rule;
  }

  Id id = new Id();

  static class Id {

    private static final String groupName = "file:id";

    private SymmetricCrypto crypto;

    private String today = DateUtil.today();

    public synchronized Long nextId() {
      if (crypto == null || !today.equals(DateUtil.today())) {
        today = DateUtil.today();
        byte[] key = SecureUtil.generateKey(SymmetricAlgorithm.DES.getValue(), today.getBytes()).getEncoded();
        crypto = new SymmetricCrypto(SymmetricAlgorithm.DES, key);
      }
      ByteBuffer buffer = ByteBuffer.allocate(Integer.BYTES);
      buffer.putInt(IdUtils.nextDayId(groupName).intValue());
      return Math.abs(ByteBuffer.wrap(crypto.encrypt(buffer.array())).getLong());
    }

  }

//  public static void main(String[] args) {
//    byte[] key = SecureUtil.generateKey(SymmetricAlgorithm.DES.getValue(),DateUtil.today().getBytes()).getEncoded();
//    System.out.println(HexUtil.encodeHexStr(key));
////    byte[] key = HexUtil.decodeHex("a359f3fe88445c192f20d573c80af163");
//    SymmetricCrypto aes = new SymmetricCrypto(SymmetricAlgorithm.DES, key);
//
//
//    ByteBuffer buffer = ByteBuffer.allocate(Integer.BYTES);
//
//    System.out.println(buffer.array().length);
//    System.out.println(aes.encrypt(buffer.array()).length);
//    buffer.clear();
//    buffer.putInt(1);
//    System.out.println(ByteBuffer.wrap(aes.encrypt(buffer.array())).getLong());
//    buffer.clear();
//    buffer.putInt(2);
//    System.out.println(ByteBuffer.wrap(aes.encrypt(buffer.array())).getLong());
//    buffer.clear();
//    buffer.putInt(3);
//    System.out.println(ByteBuffer.wrap(aes.encrypt(buffer.array())).getLong());
//  }

  SysOssVo url(SysOssVo oss, int second);

  default SysOssVo url(SysOssVo oss) {
    return url(oss, 120);
  }

  String url(Service service,String url,int second);

  default String url(Service service,String url) {
    return url(service,url, 120);
  }
}
