package com.ruoyi.service.impl;

import com.itextpdf.text.*;
import com.itextpdf.text.pdf.*;
import com.ruoyi.common.core.domain.entity.SysDictData;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.PdfBaseWriter;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.domain.vo.EnterpriseFilePdfVO;
import com.ruoyi.domain.vo.InspectionReportPdfVO;
import com.ruoyi.domain.vo.ReviewEnterpriseArchiveViewVO;
import com.ruoyi.mapper.TaskMapper;
import com.ruoyi.service.PdfTemplateManagementService;
import com.ruoyi.service.ReviewEnterpriseArchiveService;
import com.ruoyi.system.mapper.SysDictDataMapper;
import io.minio.MinioClient;
import io.minio.ObjectWriteArgs;
import io.minio.PutObjectArgs;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.*;
import java.util.List;

/**
 * PDF模板管理Service业务层处理
 * @author gxk
 */
@Service
@Transactional
public class PdfTemplateManagementServiceImpl implements PdfTemplateManagementService {

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

    @Value("${minio.url}")
    private String minioEndpoint;

    @Value("${minio.accessKey}")
    private String accessKey;

    @Value("${minio.secretKey}")
    private String secretKey;

    @Autowired
    private ReviewEnterpriseArchiveService reviewEnterpriseArchiveService;

    @Autowired
    private TaskMapper taskMapper;

    @Autowired
    private SysDictDataMapper dictDataMapper;

    /**
     * 检验报告PDF下载
     *
     * @param taskId
     * @return
     */
    @Override
    public String generateInspectionReport(Long taskId) throws Exception {
        PdfReader reader = null;
        PdfStamper ps = null;
        ByteArrayOutputStream bos = null;
        try {
            // 模板绝对路径--服务器
            String fileName = "/template/receipt_template_02_27.pdf";
            // 读取现有模板内容
            reader = new PdfReader(fileName);
            // 创建输出流
            bos = new ByteArrayOutputStream();
            // 实例化PdfStamper准备编辑pdf内容
            ps = new PdfStamper(reader, bos);

            // 获取表单所有元素
            AcroFields fields = ps.getAcroFields();

            // 设置具体字体格式的编码, 不设置的话中文可能不会显示
            BaseFont bf = BaseFont.createFont("STSongStd-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
            fields.addSubstitutionFont(bf);

            // 测试数据
            InspectionReportPdfVO inspectionReportPdfVO = requireData(taskId);

            // 渲染数据
            renderData(fields, inspectionReportPdfVO);

            //必须要调用这个,否则文档不会生成的
            ps.setFormFlattening(true);
            ps.close();

        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if (bos != null){
                bos.close();
            }
            if(reader != null){
                reader.close();
            }
        }
        assert bos != null;
        return uploadMinio(bos, "检验报告_" + taskId);
    }

    /**
     * 企业留档文件PDF下载
     * @param taskId
     * @return
     * @throws Exception
     */
    @Override
    public String generateRetentionFile(Long taskId) throws Exception {

        // 活数据查询
        EnterpriseFilePdfVO data = getEnterpriseFileData(taskId);

        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        // 新建
        PdfBaseWriter document = new PdfBaseWriter(outputStream);
        // 打开
        document.open();
        // 内容
        // 封面
        document.setParagraph(data.getInspectionCenterName(), 12f, 0, 60,15f, 120f);
        document.setParagraph("企业留档文件", 30f, Element.ALIGN_CENTER, 0,15f, 180f);
        PdfPTable customTable = new PdfPTable(new float[]{15, 50});
        customTable.setWidthPercentage(70);
        document.writeNoBorderCell("检验依据:", Element.ALIGN_LEFT, 1, 1, customTable);
        document.writeBottomCell(data.getInspectionBasis(), Element.ALIGN_LEFT, 1, 1, customTable);
        document.writeNoBorderCell("检验项目:", Element.ALIGN_LEFT, 1, 1, customTable);
        document.writeBottomCell(data.getInspectionItem(), Element.ALIGN_LEFT, 1, 1, customTable);
        document.writeNoBorderCell("创建时间:", Element.ALIGN_LEFT, 1, 1, customTable);
        document.writeBottomCell(data.getCreateTime(), Element.ALIGN_LEFT, 1, 1, customTable);
        document.writeNoBorderCell("委托单位名称:", Element.ALIGN_LEFT, 1, 1, customTable);
        document.writeBottomCell(data.getClientName(), Element.ALIGN_LEFT, 1, 1, customTable);
        document.addContent(customTable);
        document.setParagraph(data.getInspectionCenterName(), 20f, Element.ALIGN_CENTER, 0, 100f, 0f);
        // 下一页
        document.newPage();
        document.setParagraph(data.getInspectionCenterName(), 12f, 0, 30,15f, 20f);
        // 创建表格
        List<String> list = Arrays.asList("文件名称", "文件目录", "标准章节", "标准要求", "审查要点");
        float[] columnWidths = {30, 25, 25, 60, 60};
        PdfPTable table = document.createWithHeaderTable(list, columnWidths);
        // 查询数据
        List<ReviewEnterpriseArchiveViewVO> viewList = reviewEnterpriseArchiveService.view(taskId);
        for (ReviewEnterpriseArchiveViewVO view : viewList) {
            document.writeCell(view.getFileName(), Element.ALIGN_LEFT, 1, view.getItems().size(), table);
            view.getItems().forEach(item -> {
                document.writeCell(item.getCatalogue(), Element.ALIGN_LEFT, 1, 1, table);
                document.writeCell(item.getChapter(), Element.ALIGN_LEFT, 1, 1, table);
                document.writeCell(item.getStandard(), Element.ALIGN_LEFT, 1, 1, table);
                document.writeCell(item.getKeyPoint(), Element.ALIGN_LEFT, 1, 1, table);
            });
        }
        document.addContent(table);
        // 最后结尾签字
        PdfPTable signTable = new PdfPTable(new float[]{40, 15});
        signTable.setWidthPercentage(100);
        signTable.setSpacingBefore(40);
        document.writeNoBorderCell("车企负责人签字:", Element.ALIGN_RIGHT, 1, 1, signTable);
        document.writeBottomCell("", Element.ALIGN_RIGHT, 1, 1, signTable);
        document.addContent(signTable);
        // 关闭
        document.close();

        // 上传到Minio
        return uploadMinio(outputStream, "企业留档文件_" + taskId);
    }

    /**
     * 获取企业留档文件数据
     * @param taskId
     * @return
     */
    private EnterpriseFilePdfVO getEnterpriseFileData(Long taskId) {
        EnterpriseFilePdfVO enterpriseFileData = taskMapper.selectEnterpriseFileData(taskId);
        enterpriseFileData.setInspectionItem(getInspectionItemByTaskItem(enterpriseFileData.getInspectionItem()));
        return enterpriseFileData;
    }

    /**
     * 获取当前项目的检验项目
     * @return
     */
    private String getInspectionItemByTaskItem(String inspectionItem) {
        List<SysDictData> dictDataList = dictDataMapper.selectDictDataByType("inspection_item");
        List<String> list = Arrays.asList(inspectionItem.split(","));
        StringBuilder taskItem = new StringBuilder();
        for (String item : list) {
            Optional<SysDictData> first = dictDataList.stream().filter(data -> data.getDictValue().equals(item)).findFirst();
            if (first.isPresent()) {
                SysDictData dictData = first.get();
                taskItem.append(dictData.getDictLabel()).append("、");
            }
        }
        return taskItem.substring(0, taskItem.length() - 1);
    }

    /**
     * 上传到minio
     * @param outputStream 输出流
     * @param fileName 文件名
     * @return minio存储地址
     */
    private String uploadMinio(ByteArrayOutputStream outputStream, String fileName) throws Exception {
        // 1.连接Minio
        MinioClient minioClient =MinioClient.builder()
                .endpoint(minioEndpoint)
                .credentials(accessKey, secretKey)
                .build();
        // 2.上传
        // 把os流转为is流
        ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
        String newFileName = assemblyPdfName(fileName);
        PutObjectArgs putObjectArgs = PutObjectArgs.builder()
                .bucket(bucketName)
                .object(newFileName)
                .contentType("application/pdf")
                .stream(inputStream, inputStream.available(), ObjectWriteArgs.MIN_MULTIPART_SIZE).build();
        minioClient.putObject(putObjectArgs);
        return newFileName;
    }

    /**
     * 拼接pdf文件名
     * @param name
     * @return
     */
    public String assemblyPdfName(String name) {
        return StringUtils.format("{}/{}.pdf", DateUtils.datePath(), name);
    }

    /**
     * 模拟数据
     * @return
     */
    private InspectionReportPdfVO requireData(Long taskId) {
        InspectionReportPdfVO inspectionReport = taskMapper.selectInspectionReportData(taskId);
        inspectionReport.setInspectionItem(getInspectionItemByTaskItem(inspectionReport.getInspectionItem()));
        return inspectionReport;
    }

    /**
     * 渲染数据
     * @param fields
     */
    private void renderData(AcroFields fields, InspectionReportPdfVO inspectionReportPdfVO) throws Exception {
        // 取出当前对象所有属性,并赋值到模板里
        Class<? extends InspectionReportPdfVO> aClass = inspectionReportPdfVO.getClass();
        Field[] declaredFields = aClass.getDeclaredFields();
        for (Field field : declaredFields) {
            String name = field.getName();
            String key = name.replaceAll("([a-z])([A-Z])", "$1_$2").toLowerCase();
            field.setAccessible(true);
            Object o = field.get(inspectionReportPdfVO);
            if (o != null) {
                String value = o.toString();
                fields.setField(key, value);
            }
        }
    }


    /**
     * 填充模板中的数据
     * @param fields
     * @param data 是一个Map<String,String> 主要存储 key 表单模板中的单元格名 value为想要赋的值,遍历
     * @param ps
     */
    public void fillData(AcroFields fields, Map<String, String> data, PdfStamper ps) {
        try {
            for (String key : data.keySet()) {
                String value = data.get(key);
                if (key.contains("image")) {
                    addImageToPdf(key, fields, ps, value);
                    continue;
                }
                // 为字段赋值,注意字段名称是区分大小写的
                fields.setField(key, value);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 添加图片
     * @param key 应为模板名
     * @param form 动态字段
     * @param stamper
     * @param filePath 本地图片路径
     * @throws DocumentException
     * @throws IOException
     * @throws IOException
     */
    private static void addImageToPdf(String key,AcroFields form, PdfStamper stamper, String filePath) throws DocumentException, IOException, IOException {

        // 通过图片域名获取所在页和坐标,左下角为起点
        int pageNo = form.getFieldPositions(key).get(0).page;
        Rectangle signRect = form.getFieldPositions(key).get(0).position;
        float x = signRect.getLeft();
        float y = signRect.getBottom();

        // 读图片
        Image image = Image.getInstance(filePath);
        // 获取操作的页面
        PdfContentByte under = stamper.getOverContent(pageNo);
        // 根据域的大小缩放图片
        image.scaleToFit(signRect.getWidth() * 2, signRect.getHeight());
        // 添加图片并设置位置(个人通过此设置使得图片垂直水平居中,可参考,具体情况已实际为准)
        image.setAbsolutePosition(x, y);
        under.addImage(image);
    }

    /**
     * 通过属性名获取属性在对象里的值
     * @param obj
     * @param propertyName
     * @return
     * @param <T>
     */
    public static <T> T getPropertyValue(Object obj, String propertyName) {
        try {
            // 获取对象的类
            Class<?> objClass = obj.getClass();

            // 获取类的指定属性
            Field field = objClass.getDeclaredField(propertyName);

            // 设置属性为可访问
            field.setAccessible(true);

            // 获取属性的值
            @SuppressWarnings("unchecked")
            T value = (T) field.get(obj);

            return value;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

}