package com.zt.plat.module.qms.business.config.controller.admin;

import static com.zt.plat.framework.common.pojo.CommonResult.success;

import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.text.StringEscapeUtils;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.support.ResourceRegion;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.util.UriUtils;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.zt.plat.framework.common.exception.ServiceException;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.framework.common.util.servlet.ServletUtils;
import com.zt.plat.framework.datapermission.core.annotation.DeptDataPermissionIgnore;
import com.zt.plat.framework.security.config.SecurityProperties;
import com.zt.plat.framework.security.core.LoginUser;
import com.zt.plat.framework.security.core.util.SecurityFrameworkUtils;
import com.zt.plat.framework.web.core.util.WebFrameworkUtils;
import com.zt.plat.module.qms.business.config.controller.vo.GenReportBody;
import com.zt.plat.module.qms.business.config.dal.dataobject.ConfigReportTemplateDO;
import com.zt.plat.module.qms.business.config.service.ConfigReportTemplateService;
import com.zt.plat.module.qms.enums.ErrorCodeConstants;
import com.zt.plat.module.qms.framework.gridreport.GridReport;
import com.zt.plat.module.qms.framework.gridreport.PdfHelper;
import com.zt.plat.module.qms.framework.gridreport.exception.GridReportException;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import gridreport.jni.BinaryObject;
import gridreport.jni.ExportImageType;
import gridreport.jni.ExportType;
import gridreport.jni.Report;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@RestController
@RequestMapping("/qms/config-report-template")
@DeptDataPermissionIgnore(enable = "true")
public class GridReportController {

    public static final String WR_PREFIX = "_WR_";

	@Resource
	private GridReport gridReport;
	
	@Resource
	private SecurityProperties securityProperties;

    @Resource
    private ConfigReportTemplateService configReportTemplateService;

    @GetMapping("/info")
    @Operation(summary = "获取报表信息")
    public CommonResult<?> info() {
        Map<String, Object> data = new HashMap<>();
        try {
            String moduleInfo = Report.getModuleInfo();
            String moduleVersion = Report.getModuleVersion();
            String modulePath = Report.getModulePath();
            data.put("moduleInfo", moduleInfo);
            data.put("moduleVersion", moduleVersion);
            data.put("modulePath", modulePath);
        } catch (Exception e) {
        }
        return success(data);
    }

    /***
     * 预览报表
     * @param reportId
     * @param reportKey
     * @param dataUrl
     * @param isCrypto
     * @param type
     * @return
     * @throws Exception
     */
    @GetMapping("/preview")
    @Operation(summary = "预览报表")
    public ResponseEntity<org.springframework.core.io.Resource> preview(Long reportId, String reportKey, String dataUrl, Boolean isCrypto, String type) throws Exception {
        HttpServletRequest request = ServletUtils.getRequest();
        ConfigReportTemplateDO gridppReportFile = null;
        if (reportId != null) {
            gridppReportFile = configReportTemplateService.getConfigReportTemplate(reportId);
        } else if (StrUtil.isNotBlank(reportKey)) {
            gridppReportFile = configReportTemplateService.getConfigReportTemplateByKey(reportKey);
        }
        if (ObjectUtil.isEmpty(gridppReportFile)) {
        	throw new ServiceException(ErrorCodeConstants.CONFIG_REPORT_TEMPLATE_NOT_EXISTS);
        }
        String reportData = StringEscapeUtils.unescapeHtml4(gridppReportFile.getData());
        
        //报表参数
        Map<String, String> reportParams = new HashMap<>();
        //获取当前用户姓名
        LoginUser user = SecurityFrameworkUtils.getLoginUser();
        String realName = "";
        if (user != null) {
            realName = SecurityFrameworkUtils.getLoginUserNickname();
        }
        reportParams.put("operName", realName);
        
        if (StrUtil.isNotBlank(dataUrl)) {
            log.info("url:" + dataUrl);
            dataUrl = StringEscapeUtils.unescapeHtml4(dataUrl);
            log.info("url:" + dataUrl);
            //处理url参数
            if(dataUrl.contains("?")) {
                String urlParam = dataUrl.split("\\?")[1];
                String[] urlVks = urlParam.split("&");
                for (int i = 0; i < urlVks.length; i++) {
                    String vk = urlVks[i];
                    if (StrUtil.isNotBlank(vk)) {
                        String[] vkSplit = vk.split("=");
                        if(vkSplit.length == 2){
                            reportParams.put(vkSplit[0], vkSplit[1]);
                        }
                    }
                }
            }
            
            Map<String, String> headers = new HashMap<>();
            String token = SecurityFrameworkUtils.obtainAuthorization(request,
                    securityProperties.getTokenHeader(), securityProperties.getTokenParameter());
            headers.put(securityProperties.getTokenHeader(), token);
//            headers.put(WebFrameworkUtils.HEADER_VISIT_COMPANY_ID, user.getVisitCompanyId() + "");
//            headers.put(WebFrameworkUtils.HEADER_VISIT_DEPT_ID, user.getVisitDeptId() + "");
            
            HttpResponse res = HttpRequest.get(dataUrl)
                        .addHeaders(headers)
                        .setConnectionTimeout(60 * 1000)
                        .setReadTimeout(120 * 1000)
                        .execute();
            if (res.getStatus() != 200) {
                log.error("报表数据，网络请求错误！", res.body());
                throw new GridReportException("报表数据，网络请求错误！");
            }
            String body = res.body();
            CommonResult<?> dataR = JSON.parseObject(body, CommonResult.class);
            if (dataR != null && dataR.getCode() == 0) {
                if (ArrayUtil.isArray(dataR.getData()) || dataR.getData() instanceof Collection) {
                    reportData = JSON.toJSONString(dataR);
                } else {
                    reportData = JSON.toJSONString(dataR.getData());
                }
            } else {
                log.error("获取报表数据错误！", dataR.getMsg());
                log.error("报表数据：" + reportData);
                throw new GridReportException("获取报表数据错误！");
            }
        }
        
        
        BinaryObject binaryObject = gridReport.genReport(gridppReportFile.getContent(), reportData, reportParams, ExportType.valueOf("OFD".equals(type.toUpperCase()) ? "PDF" : type.toUpperCase()));
        MediaType mediaType = null;
        byte[] docBytes = null;
        //文件扩展名
        String fileExtName = type.toLowerCase();
        switch (type.toUpperCase()) {
            case "PDF":
                mediaType = MediaType.APPLICATION_PDF;
                docBytes = PdfHelper.modifyDocInfo(binaryObject.getDataBuf());
                break;
            case "OFD":
                mediaType = MediaType.APPLICATION_OCTET_STREAM;
                docBytes = PdfHelper.modifyDocInfo(binaryObject.getDataBuf());
                break;
            case "RTF":
                fileExtName = "doc";
                mediaType = MediaType.APPLICATION_OCTET_STREAM;
                docBytes = binaryObject.getDataBuf();
                break;
            default:
                mediaType = MediaType.APPLICATION_OCTET_STREAM;
                docBytes = binaryObject.getDataBuf();
                break;
        }
        
        return browser(new ByteArrayResource(docBytes), gridppReportFile.getName() + "." + fileExtName, mediaType);
    }

    @GetMapping("/previewDocs")
    @Operation(summary = "报告预览报表")
    public ResponseEntity<org.springframework.core.io.Resource> previewDocs(Long reportId, String reportKey, String dataUrl, Boolean isCrypto, String type) throws Exception {
        HttpServletRequest request = ServletUtils.getRequest();
        ConfigReportTemplateDO gridppReportFile = null;
        if (reportId != null) {
            gridppReportFile = configReportTemplateService.getConfigReportTemplate(reportId);
        } else if (StrUtil.isNotBlank(reportKey)) {
            gridppReportFile = configReportTemplateService.getConfigReportTemplateByKey(reportKey);
        }
        if (ObjectUtil.isEmpty(gridppReportFile)) {
            throw new GridReportException("未找到报表模板！");
        }
        String reportData = StringEscapeUtils.unescapeHtml4(gridppReportFile.getData());
        
        //报表参数
        Map<String, String> reportParams = new HashMap<>();
        //获取当前用户姓名
        LoginUser user = SecurityFrameworkUtils.getLoginUser();
        String realName = "";
        if (user != null) {
            realName = SecurityFrameworkUtils.getLoginUserNickname();
        }
        reportParams.put("operName", realName);
        
        if (StrUtil.isNotBlank(dataUrl)) {
            log.info("url:" + dataUrl);
            dataUrl = StringEscapeUtils.unescapeHtml4(dataUrl);
            log.info("url:" + dataUrl);
            

            Map<String, String> headers = new HashMap<>();
            String token = SecurityFrameworkUtils.obtainAuthorization(request,
                    securityProperties.getTokenHeader(), securityProperties.getTokenParameter());
            headers.put(securityProperties.getTokenHeader(), token);
//            headers.put(WebFrameworkUtils.HEADER_VISIT_COMPANY_ID, user.getVisitCompanyId() + "");
//            headers.put(WebFrameworkUtils.HEADER_VISIT_DEPT_ID, user.getVisitDeptId() + "");
            HttpResponse res = HttpRequest.get(dataUrl)
                        .addHeaders(headers)
                        .setConnectionTimeout(60 * 1000)
                        .setReadTimeout(120 * 1000)
                        .execute();
            if (res.getStatus() != 200) {
                log.error("报表数据，网络请求错误！", res.body());
                throw new GridReportException("报表数据，网络请求错误！");
            }
            String body = res.body();
            CommonResult<?> dataR = JSON.parseObject(body, CommonResult.class);
            if (dataR != null && dataR.getCode() == 0) {
                if (ArrayUtil.isArray(dataR.getData()) || dataR.getData() instanceof Collection) {
                    reportData = JSON.toJSONString(dataR);
                } else {
                    
                    Map<String, Object> map = BeanUtil.beanToMap(dataR.getData());
                    for (Map.Entry<String,Object> obj : BeanUtil.beanToMap(map.get("reportParams")).entrySet()) {
                        reportParams.put(obj.getKey(), obj.getValue() == null ? "": obj.getValue().toString());
                    }
                    reportData = JSON.toJSONString(dataR.getData());
                }
            } else {
                log.error("获取报表数据错误！", dataR.getMsg());
                log.error("报表数据：" + reportData);
                throw new GridReportException("获取报表数据错误！");
            }
            
        }

        BinaryObject binaryObject = gridReport.genReport(gridppReportFile.getContent(), reportData, reportParams, ExportType.valueOf("OFD".equals(type.toUpperCase()) ? "PDF" : type.toUpperCase()));
        MediaType mediaType = null;
        byte[] docBytes = null;
        //文件扩展名
        String fileExtName = type.toLowerCase();
        switch (type.toUpperCase()) {
            case "PDF":
                mediaType = MediaType.APPLICATION_PDF;
                docBytes = PdfHelper.modifyDocInfo(binaryObject.getDataBuf());
                break;
            case "OFD":
                mediaType = MediaType.APPLICATION_OCTET_STREAM;
                docBytes = PdfHelper.modifyDocInfo(binaryObject.getDataBuf());
                break;
            case "RTF":
                fileExtName = "doc";
                mediaType = MediaType.APPLICATION_OCTET_STREAM;
                docBytes = binaryObject.getDataBuf();
                break;
            case "IMG":
                fileExtName = "png";
                mediaType = MediaType.IMAGE_PNG;
                docBytes = binaryObject.getDataBuf();
                break;
            default:
                mediaType = MediaType.APPLICATION_OCTET_STREAM;
                docBytes = binaryObject.getDataBuf();
                break;
        }
        
        return browser(new ByteArrayResource(docBytes), gridppReportFile.getName() + "." + fileExtName, mediaType);
    }

    @GetMapping("/export")
    @Operation(summary = "导出报表")
    public ResponseEntity<ResourceRegion> export(Long reportId, String reportKey, String exportName, String dataUrl, Boolean isCrypto, String type) throws Exception {
        HttpServletRequest request = ServletUtils.getRequest();
        ConfigReportTemplateDO gridppReportFile = null;
        if (reportId != null) {
            gridppReportFile = configReportTemplateService.getConfigReportTemplate(reportId);
        } else if (StrUtil.isNotBlank(reportKey)) {
            gridppReportFile = configReportTemplateService.getConfigReportTemplateByKey(reportKey);
        }
        if (ObjectUtil.isEmpty(gridppReportFile)) {
            throw new GridReportException("未找到报表模板！");
        }
        String reportData = StringEscapeUtils.unescapeHtml4(gridppReportFile.getData());

        //报表参数
        Map<String, String> reportParams = new HashMap<>();
        //获取当前用户姓名
        LoginUser user = SecurityFrameworkUtils.getLoginUser();
        String realName = "";
        if (user != null) {
            realName = SecurityFrameworkUtils.getLoginUserNickname();
        }
        reportParams.put("operName", realName);
                
        if (StrUtil.isNotBlank(dataUrl)) {
            dataUrl = StringEscapeUtils.unescapeHtml4(dataUrl);
            //处理url参数
            if (dataUrl.contains("?")) {
                String urlParam = dataUrl.split("\\?")[1];
                String[] urlVks = urlParam.split("&");
                for (int i = 0; i < urlVks.length; i++) {
                    String vk = urlVks[i];
                    if (StrUtil.isNotBlank(vk)) {
                        String[] vkSplit = vk.split("=");
                        if(vkSplit.length == 2){
                            reportParams.put(vkSplit[0], vkSplit[1]);
                        }
                    }
                }
            }
            Map<String, String> headers = new HashMap<>();
            String token = SecurityFrameworkUtils.obtainAuthorization(request,
                    securityProperties.getTokenHeader(), securityProperties.getTokenParameter());
            headers.put(securityProperties.getTokenHeader(), token);
//            headers.put(WebFrameworkUtils.HEADER_VISIT_COMPANY_ID, user.getVisitCompanyId() + "");
//            headers.put(WebFrameworkUtils.HEADER_VISIT_DEPT_ID, user.getVisitDeptId() + "");
            HttpResponse res = HttpRequest.get(dataUrl)
                        .addHeaders(headers)
                        .setConnectionTimeout(60 * 1000)
                        .setReadTimeout(120 * 1000)
                        .execute();
            if (res.getStatus() != 200) {
                log.error("报表数据，网络请求错误！", res.body());
                throw new GridReportException("报表数据，网络请求错误！");
            }
            String body = res.body();
            CommonResult<?> dataR = JSON.parseObject(body, CommonResult.class);
            if (dataR != null && dataR.getCode() == 0) {
                if (ArrayUtil.isArray(dataR.getData()) || dataR.getData() instanceof Collection) {
                    reportData = JSON.toJSONString(dataR);
                } else {
                    reportData = JSON.toJSONString(dataR.getData());
                }
            } else {
                log.error("获取报表数据错误！", dataR.getMsg());
                throw new GridReportException("获取报表数据错误！");
            }
        }
        
        BinaryObject binaryObject = gridReport.genReport(gridppReportFile.getContent(), reportData, reportParams, ExportType.valueOf("OFD".equals(type.toUpperCase()) ? "PDF" : type.toUpperCase()));
        byte[] docBytes = null;
        //文件扩展名
        String fileExtName = type.toLowerCase();
        switch (type.toUpperCase()) {
            case "PDF":
                docBytes = PdfHelper.modifyDocInfo(binaryObject.getDataBuf());
                break;
            case "OFD":
                docBytes = PdfHelper.modifyDocInfo(binaryObject.getDataBuf());
                break;
            case "RTF":
                fileExtName = "doc";
                docBytes = binaryObject.getDataBuf();
                break;
            case "IMG":
                fileExtName = "png";
                docBytes = binaryObject.getDataBuf();
                break;
            default:
                docBytes = binaryObject.getDataBuf();
                break;
        }
        String fileName = gridppReportFile.getName();
        if (StrUtil.isNotBlank(exportName)) {
            fileName = exportName;
        }
        return download(new ByteArrayResource(docBytes), fileName + "." + fileExtName);
    }
    
    /**
     * 生成Base64图片
     * @return
     */
    @PostMapping("/gen-report-png-image-base64")
    @Operation(summary = "生成Base64图片")
    public CommonResult<?> genReportPngImageBase64(@RequestBody GenReportBody body) {
        ConfigReportTemplateDO gridppReportFile = null;
        if (body.getReportId() != null) {
            gridppReportFile = configReportTemplateService.getConfigReportTemplate(body.getReportId());
        } else if (StrUtil.isNotBlank(body.getReportKey())) {
            gridppReportFile = configReportTemplateService.getConfigReportTemplateByKey(body.getReportKey());
        }
        if (ObjectUtil.isEmpty(gridppReportFile)) {
            throw new ServiceException(ErrorCodeConstants.CONFIG_REPORT_TEMPLATE_NOT_EXISTS);
        }

        String imgBase64 = null;
        try {
			BinaryObject binaryObject = gridReport.genReport(gridppReportFile.getContent(), body.getReportDataJson(), body.getReportParams(), ExportType.IMG, ExportImageType.PNG, body.getImgDpi());
			imgBase64 = binaryObject.getAsBase64Text();
		} catch (Exception e) {
			log.error("生成base64图片失败", e);
		}
        if (StringUtils.isBlank(imgBase64)) {
			return CommonResult.error(1_032_100_000, "生成Base64图片失败");
		}
    	return success(imgBase64);
    }
    
    /**
     * 生成Base64图片
     * @return
     */
    @PostMapping("/gen-report-png-image-base64-by-pdf")
    @Operation(summary = "生成Base64图片")
    public CommonResult<?> genReportPngImageBase64ByPdf(@RequestBody GenReportBody body) {
        ConfigReportTemplateDO gridppReportFile = null;
        if (body.getReportId() != null) {
            gridppReportFile = configReportTemplateService.getConfigReportTemplate(body.getReportId());
        } else if (StrUtil.isNotBlank(body.getReportKey())) {
            gridppReportFile = configReportTemplateService.getConfigReportTemplateByKey(body.getReportKey());
        }
        if (ObjectUtil.isEmpty(gridppReportFile)) {
        	throw new ServiceException(ErrorCodeConstants.CONFIG_REPORT_TEMPLATE_NOT_EXISTS);
        }

        String imgBase64 = null;
        try {
			BinaryObject binaryObject = gridReport.genReport(gridppReportFile.getContent(), body.getReportDataJson(), body.getReportParams(), ExportType.PDF);
			byte[] pngBytes = PdfHelper.pdfToPng(binaryObject.getDataBuf(), body.getImgDpi());
			//String encodeHexStr = HexUtil.encodeHexStr(pngBytes);
			//System.out.println(encodeHexStr);
			imgBase64 = Base64.encode(pngBytes);
		} catch (Exception e) {
			log.error("生成base64图片失败", e);
		}
        if (StringUtils.isBlank(imgBase64)) {
			return CommonResult.error(1_032_100_000, "生成Base64图片失败");
		}
    	return success(imgBase64);
    }

    /**
     * 下载文件
     *
     * @param file 文件
     * @return {ResponseEntity}
     * @throws IOException io异常
     */
    protected ResponseEntity<ResourceRegion> download(File file) throws IOException {
        String fileName = file.getName();
        return download(file, fileName);
    }

    /**
     * 下载
     *
     * @param file     文件
     * @param fileName 生成的文件名
     * @return {ResponseEntity}
     * @throws IOException io异常
     */
    protected ResponseEntity<ResourceRegion> download(File file, String fileName) throws IOException {
    	org.springframework.core.io.Resource resource = new FileSystemResource(file);
        return download(resource, fileName);
    }

    /**
     * 下载
     *
     * @param resource 资源
     * @param fileName 生成的文件名
     * @return {ResponseEntity}
     * @throws IOException io异常
     */
    protected ResponseEntity<ResourceRegion> download(org.springframework.core.io.Resource resource, String fileName) throws IOException {
        HttpServletRequest request = ServletUtils.getRequest();
        String header = request.getHeader(HttpHeaders.USER_AGENT);
        // 避免空指针
        header = header == null ? StringPool.EMPTY : header.toUpperCase();
        HttpStatus status= HttpStatus.OK;
        // 断点续传
        long position = 0;
        long count = resource.contentLength();
        String range = request.getHeader(HttpHeaders.RANGE);
        if (null != range) {
            status = HttpStatus.PARTIAL_CONTENT;
            String[] rangeRange = range.replace("bytes=", StringPool.EMPTY).split(StringPool.DASH);
            position = Long.parseLong(rangeRange[0]);
            if (rangeRange.length > 1) {
                long end = Long.parseLong(rangeRange[1]);
                count = end - position + 1;
            }
        }
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
        String encodeFileName = UriUtils.encode(fileName, StandardCharsets.UTF_8);
        // 兼容各种浏览器下载：
        // https://blog.robotshell.org/2012/deal-with-http-header-encoding-for-file-download/
        String disposition = "attachment;" +
            "filename=\"" + encodeFileName + "\";" +
            "filename*=utf-8''" + encodeFileName;
        headers.set(HttpHeaders.CONTENT_DISPOSITION, disposition);
        return new ResponseEntity<>(new ResourceRegion(resource, position, count), headers, status);
    }

    /**
     * 浏览器打开
     *
     * @param resource 资源
     * @param fileName 生成的文件名
     * @return {ResponseEntity}
     * @throws IOException io异常
     */
    protected ResponseEntity<org.springframework.core.io.Resource> browser(org.springframework.core.io.Resource resource, String fileName, MediaType mediaType) throws IOException {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(mediaType);
        headers.setContentLength(resource.contentLength());
        String encodeFileName = UriUtils.encode(fileName, StandardCharsets.UTF_8);
        // 兼容各种浏览器下载：
        // https://blog.robotshell.org/2012/deal-with-http-header-encoding-for-file-download/
        String disposition = "inline;" +
            "filename=\"" + encodeFileName + "\";" +
            "filename*=utf-8''" + encodeFileName;
        headers.set(HttpHeaders.CONTENT_DISPOSITION, disposition);
        return new ResponseEntity<>(resource, headers, HttpStatus.OK);
    }

}
