package com.hotwj.platform.config.vehicleThreeInspect.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.fasterxml.jackson.core.type.TypeReference;
import com.hotwj.platform.common.utils.PdfWatermarkUtil;
import com.hotwj.platform.config.vehicleThreeInspect.domain.HotVehicleThreeInspect;
import com.hotwj.platform.config.vehicleThreeInspect.domain.bo.HotVehicleThreeInspectBo;
import com.hotwj.platform.config.vehicleThreeInspect.domain.vo.HotVehicleThreeInspectPrintVo;
import com.hotwj.platform.config.vehicleThreeInspect.mapper.HotVehicleThreeInspectMapper;
import com.hotwj.platform.config.vehicleThreeInspect.service.IHotVehicleThreeInspectPrintService;
import com.hotwj.platform.config.vehicleThreeInspectConfig.domain.HotVehicleThreeInspectConfig;
import com.hotwj.platform.config.vehicleThreeInspectConfig.mapper.HotVehicleThreeInspectConfigMapper;
import com.hotwj.platform.reportStatistics.integration.GotenbergClient;
import com.hotwj.platform.reportStatistics.template.VelocityTemplateRenderer;
import com.hotwj.platform.resourceManagement.company.domain.vo.SysCompanyVo;
import com.hotwj.platform.resourceManagement.company.service.ISysCompanyService;
import com.hotwj.platform.resourceManagement.vehicleManagement.domain.HotVehicle;
import com.hotwj.platform.resourceManagement.vehicleManagement.mapper.HotVehicleMapper;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.velocity.VelocityContext;
import org.dromara.common.core.domain.dto.OssDTO;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.service.OssService;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.core.utils.file.FileUtils;
import org.dromara.common.json.utils.JsonUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.FileWriter;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.*;

@Slf4j
@Service
@RequiredArgsConstructor
public class HotVehicleThreeInspectPrintServiceImpl implements IHotVehicleThreeInspectPrintService {

    private final HotVehicleThreeInspectMapper threeInspectMapper;
    private final HotVehicleMapper vehicleMapper;
    private final HotVehicleThreeInspectConfigMapper threeInspectConfigMapper;
    private final GotenbergClient gotenbergClient;
    private final VelocityTemplateRenderer velocityTemplateRenderer;
    private final OssService ossService;
    private final ISysCompanyService sysCompanyService;

    @Value("${dromara.app.name:HOT交通安全管理清单平台}")
    private String appName;

    @Override
    public void exportPdf(HotVehicleThreeInspectBo bo, HttpServletResponse response) {
        // Use custom XML query to get flattened VO list
        List<HotVehicleThreeInspectPrintVo> vos = threeInspectMapper.selectPrintList(bo);
        if (CollUtil.isEmpty(vos)) {
            throw new ServiceException("数据不存在");
        }

        // Generate PDF
        VelocityContext context = new VelocityContext();
        context.put("list", vos);
        context.put("appName", appName);
        context.put("printTime", DateUtil.now());

        String bodyHtml = velocityTemplateRenderer.render("templates/vehicle/vehicleThreeInspectRecord.html.vm", context);

        // Merge into landscape layout
        VelocityContext layoutContext = new VelocityContext();
        layoutContext.put("title", appName + "车辆三检记录表");
        List<String> bodies = new ArrayList<>();
        bodies.add(bodyHtml);
        layoutContext.put("bodies", bodies);

        String finalHtml = velocityTemplateRenderer.render("templates/layout/printLayoutLandscape.html.vm", layoutContext);

        Long companyId = bo.getCompanyId();
        if (companyId == null && bo.getVehicleId() != null) {
            HotVehicle vehicle = vehicleMapper.selectById(String.valueOf(bo.getVehicleId()));
            if (vehicle != null) {
                companyId = vehicle.getCompanyId();
            }
        }

        try {
            byte[] pdfBytes = convertAndWatermarkLandscape(finalHtml, companyId);

            response.setContentType("application/pdf");
            String fileName = "车辆三检记录表.pdf";
            FileUtils.setAttachmentResponseHeader(response, fileName);
            response.getOutputStream().write(pdfBytes);
        } catch (Exception e) {
            log.error("Export PDF failed", e);
            throw new ServiceException("导出PDF失败");
        }
    }

    @Override
    public void exportFormPdf(Long inspectId, HttpServletResponse response) {
        if (inspectId == null) {
            throw new ServiceException("inspectId不能为空");
        }

        // 1. 查询该车辆、该日期的所有三检记录 (出车前、行车中、收车后)
        // inspectId 是三检任务的唯一标识，关联不同阶段的记录
        List<HotVehicleThreeInspect> inspectList = threeInspectMapper.selectList(
                Wrappers.lambdaQuery(HotVehicleThreeInspect.class)
                        .eq(HotVehicleThreeInspect::getInspectId, inspectId)
        );

        if (CollUtil.isEmpty(inspectList)) {
            throw new ServiceException("检查记录不存在");
        }

        // 取第一条记录作为基础信息来源
        HotVehicleThreeInspect originInspect = inspectList.get(0);

        // 2. 准备基础数据
        Long companyId = originInspect.getCompanyId();
        HotVehicle vehicle = null;
        if (originInspect.getVehicleId() != null) {
            vehicle = vehicleMapper.selectById(String.valueOf(originInspect.getVehicleId()));
            if (companyId == null && vehicle != null) {
                companyId = vehicle.getCompanyId();
            }
        }
        SysCompanyVo company = companyId == null ? null : sysCompanyService.queryById(companyId);

        // 4. 分组处理各阶段数据
        // Map<Phase, Data>
        Map<Long, Map<String, Object>> phaseDataMap = new HashMap<>();

        // 预加载配置 (一次性加载该公司的所有配置，或者分阶段加载)
        // 这里简化逻辑：获取所有涉及的 keys，统一加载配置
        List<HotVehicleThreeInspectConfig> allConfigs = threeInspectConfigMapper.selectList(
                Wrappers.lambdaQuery(HotVehicleThreeInspectConfig.class)
                        .eq(HotVehicleThreeInspectConfig::getCompanyId, companyId)
                        .eq(HotVehicleThreeInspectConfig::getIsDeleted, 0)
        );
        Map<Long, HotVehicleThreeInspectConfig> configMap = new HashMap<>();
        if (allConfigs != null) {
            for (HotVehicleThreeInspectConfig cfg : allConfigs) {
                configMap.put(cfg.getId(), cfg);
            }
        }

        // 遍历记录处理
        HotVehicleThreeInspect phase1 = null;
        HotVehicleThreeInspect phase2 = null;
        HotVehicleThreeInspect phase3 = null;

        // 对 inspectList 按创建时间排序（虽然数据库查出来可能是乱序，我们希望取最新的）
        // 实际上数据库一般是按插入顺序，但为了保险，可以在代码里比较 inspectTime 或 createTime
        // 这里直接遍历，如果有重复阶段，取 inspectTime 或 id 最大的

        // 先按阶段分组并取最新
        Map<Long, HotVehicleThreeInspect> latestInspectMap = new HashMap<>();
        for (HotVehicleThreeInspect inspect : inspectList) {
            Long phase = inspect.getInspectPhase();
            if (phase == null) continue;

            HotVehicleThreeInspect existing = latestInspectMap.get(phase);
            if (existing == null) {
                latestInspectMap.put(phase, inspect);
            } else {
                // 比较逻辑：优先取 id 大的（通常意味着后创建），或者 inspectTime 晚的
                // 这里简单用 id 比较
                if (inspect.getId() > existing.getId()) {
                    latestInspectMap.put(phase, inspect);
                }
            }
        }

        // 仅处理最新的记录
        for (Map.Entry<Long, HotVehicleThreeInspect> entry : latestInspectMap.entrySet()) {
            Long phase = entry.getKey();
            HotVehicleThreeInspect inspect = entry.getValue();

            if (phase == 1L) phase1 = inspect;
            else if (phase == 2L) phase2 = inspect;
            else if (phase == 3L) phase3 = inspect;

            Map<String, Object> data = new HashMap<>();
            data.put("inspect", inspect);

            // 解析检查项
            LinkedHashMap<String, String> resultMap = parseInspectResult(inspect.getInspectResultJson());
            List<Map<String, Object>> items = new ArrayList<>();
            int index = 1;
            for (Map.Entry<String, String> entry1 : resultMap.entrySet()) {
                String key = entry1.getKey();
                if (StringUtils.isBlank(key)) continue;

                Long configId = parseLong(key);
                HotVehicleThreeInspectConfig cfg = configId == null ? null : configMap.get(configId);
                // 过滤掉不属于当前阶段的配置（理论上 json 里存的应该就是对应的，但双重校验更安全）
                if (cfg != null && !cfg.getInspectType().equals(phase)) {
                    // 严格模式下可以 continue，但为了兼容性，如果配置存在就显示
                }

                String title = cfg != null ? cfg.getTitle() : key;
                Map<String, Object> itemRow = new HashMap<>();
                itemRow.put("index", index++);
                itemRow.put("title", StringUtils.blankToDefault(title, ""));
                itemRow.put("result", StringUtils.blankToDefault(entry1.getValue(), ""));
                items.add(itemRow);
            }
            data.put("items", items);

            // 图片
            data.put("attachImages", resolveImages(inspect.getAttachImgUrl()));

            // 签名
            data.put("inspectorSignUrl", resolveMaybeOssUrl(inspect.getInspectorSignImgUrl()));
            data.put("auditorSignUrl", resolveMaybeOssUrl(inspect.getAuditorSignImgUrl()));

            // 其它字段
            data.put("inspectTime", inspect.getInspectTime() == null ? "" : DateUtil.formatDateTime(inspect.getInspectTime()));
            data.put("inspectorName", StringUtils.blankToDefault(inspect.getDriverName(), ""));
            data.put("auditResult", StringUtils.blankToDefault(inspect.getAuditResult(), ""));
            data.put("auditorName", StringUtils.blankToDefault(inspect.getAuditorName(), ""));
            data.put("auditTime", inspect.getAuditTime() == null ? "" : DateUtil.formatDateTime(inspect.getAuditTime()));
            data.put("hasHiddenDanger", resolveYesNo(inspect.getHasHiddenDanger()));
            data.put("isFalseReport", resolveYesNo(inspect.getIsFalseReport()));

            phaseDataMap.put(phase, data);
        }

        VelocityContext ctx = new VelocityContext();
        ctx.put("printTime", DateUtil.now());
        ctx.put("companyName", company != null ? StringUtils.blankToDefault(company.getCompanyName(), "") : "");
        ctx.put("plateNumber", vehicle != null ? StringUtils.blankToDefault(vehicle.getPlateNumber(), "") : "");

        ctx.put("phase1", phaseDataMap.get(1L));
        ctx.put("phase2", phaseDataMap.get(2L));
        ctx.put("phase3", phaseDataMap.get(3L));

        // 总体审核信息（通常取最后阶段或合并显示？根据模板需求，如果模板是分栏显示，则各显示各的）
        // 如果模板底部只有一个审核区，通常取 phase 1 的审核信息或者最后一次的？
        // 根据截图，三个表单是顺序排列的，每个都有自己的签名区。
        // 所以模板里应该循环或者按顺序 block 显示。

        String bodyHtml = velocityTemplateRenderer.render("templates/vehicle/vehicleThreeInspectForm.html.vm", ctx);
        VelocityContext layoutContext = new VelocityContext();
        layoutContext.put("title", "车辆三检检查表单");
        List<String> bodies = new ArrayList<>();
        bodies.add(bodyHtml);
        layoutContext.put("bodies", bodies);
        String finalHtml = velocityTemplateRenderer.render("templates/layout/printLayout.html.vm", layoutContext);
        try {
            byte[] pdfBytes = convertAndWatermark(finalHtml, companyId);
            response.setContentType("application/pdf");
            String fileName = "车辆三检检查表单.pdf";
            FileUtils.setAttachmentResponseHeader(response, fileName);
            response.getOutputStream().write(pdfBytes);
        } catch (Exception e) {
            log.error("Export form PDF failed", e);
            throw new ServiceException("导出PDF失败");
        }
    }

    private byte[] convertAndWatermarkLandscape(String htmlContent, Long companyId) throws Exception {
        File temp = Files.createTempFile("three-inspect-print-land-", ".html").toFile();
        try {
            try (FileWriter fw = new FileWriter(temp, StandardCharsets.UTF_8)) {
                fw.write(htmlContent);
            }

            String footer = """
                    <div style="font-size:10px;color:#000;width:100%;padding:0 1cm;">
                        <span style="float:left;color:#888;">平台信息依实为准，信息使用自判</span>
                        <span style="float:right;">第 <span class='pageNumber'></span> / <span class='totalPages'></span> 页</span>
                    </div>
                    """;

            byte[] pdf = gotenbergClient.convertHtmlToPdf(temp, footer, true);
            byte[] watermarked = PdfWatermarkUtil.addWatermarkLandscape(pdf, appName);
            byte[] sealBytes = loadCompanySeal(companyId);
            if (sealBytes == null) {
                return watermarked;
            }
            return PdfWatermarkUtil.addSealBottomRight(watermarked, sealBytes);
        } finally {
            if (temp.exists()) {
                if (!temp.delete()) {
                    log.warn("Failed to delete temp file: {}", temp.getAbsolutePath());
                }
            }
        }
    }

    private byte[] convertAndWatermark(String htmlContent, Long companyId) throws Exception {
        File temp = Files.createTempFile("three-inspect-print-", ".html").toFile();
        try {
            try (FileWriter fw = new FileWriter(temp, StandardCharsets.UTF_8)) {
                fw.write(htmlContent);
            }
            String footer = """
                    <div style="font-size:10px;color:#000;width:100%;padding:0 1cm;">
                        <span style="float:left;color:#888;">平台信息依实为准，信息使用自判</span>
                        <span style="float:right;">第 <span class='pageNumber'></span> / <span class='totalPages'></span> 页</span>
                    </div>
                    """;
            byte[] pdf = gotenbergClient.convertHtmlToPdf(temp, footer);
            byte[] watermarked = PdfWatermarkUtil.addWatermark(pdf, appName);
            byte[] sealBytes = loadCompanySeal(companyId);
            if (sealBytes == null) {
                return watermarked;
            }
            return PdfWatermarkUtil.addSealBottomRight(watermarked, sealBytes);
        } finally {
            if (temp.exists()) {
                if (!temp.delete()) {
                    log.warn("Failed to delete temp file: {}", temp.getAbsolutePath());
                }
            }
        }
    }

    private LinkedHashMap<String, String> parseInspectResult(String json) {
        if (StringUtils.isBlank(json)) {
            return new LinkedHashMap<>();
        }
        try {
            Map<String, String> map = JsonUtils.parseObject(json, new TypeReference<Map<String, String>>() {
            });
            if (map == null) {
                return new LinkedHashMap<>();
            }
            return new LinkedHashMap<>(map);
        } catch (Exception e) {
            return new LinkedHashMap<>();
        }
    }

    private Map<Long, HotVehicleThreeInspectConfig> loadConfigs(Long companyId, Long inspectPhase, List<String> keys) {
        if (CollUtil.isEmpty(keys)) {
            return new HashMap<>();
        }
        List<Long> ids = new ArrayList<>();
        for (String key : keys) {
            Long id = parseLong(key);
            if (id != null) {
                ids.add(id);
            }
        }
        if (ids.isEmpty()) {
            return new HashMap<>();
        }
        List<HotVehicleThreeInspectConfig> list = threeInspectConfigMapper.selectList(
                Wrappers.lambdaQuery(HotVehicleThreeInspectConfig.class)
                        .eq(companyId != null, HotVehicleThreeInspectConfig::getCompanyId, companyId)
                        .eq(inspectPhase != null, HotVehicleThreeInspectConfig::getInspectType, inspectPhase)
                        .in(HotVehicleThreeInspectConfig::getId, ids)
        );
        Map<Long, HotVehicleThreeInspectConfig> map = new HashMap<>();
        for (HotVehicleThreeInspectConfig cfg : list) {
            if (cfg != null && cfg.getId() != null) {
                map.put(cfg.getId(), cfg);
            }
        }
        return map;
    }

    private Long parseLong(String value) {
        if (StringUtils.isBlank(value)) {
            return null;
        }
        try {
            return Long.parseLong(value.trim());
        } catch (Exception e) {
            return null;
        }
    }

    private String resolvePhaseLabel(Long phase) {
        if (phase == null) {
            return "出车";
        }
        if (phase == 2L) {
            return "行车中";
        }
        if (phase == 3L) {
            return "收车后";
        }
        return "出车前";
    }

    private String resolveYesNo(Long value) {
        if (value == null) {
            return "";
        }
        return value == 1L ? "是" : "否";
    }

    private List<String> resolveImages(String value) {
        List<String> result = new ArrayList<>();
        if (StringUtils.isBlank(value)) {
            return result;
        }
        String urls = value;
        if (!(value.contains("http://") || value.contains("https://"))) {
            urls = ossService.selectUrlByIds(value);
        }
        if (StringUtils.isBlank(urls)) {
            return result;
        }
        for (String s : urls.split(",")) {
            String t = s == null ? "" : s.trim();
            if (StringUtils.isNotBlank(t)) {
                result.add(t);
            }
        }
        return result;
    }

    private String resolveMaybeOssUrl(String value) {
        if (StringUtils.isBlank(value)) {
            return null;
        }
        String first = value.split(",")[0].trim();
        if (StringUtils.isBlank(first)) {
            return null;
        }
        if (StringUtils.ishttp(first)) {
            return first;
        }
        if (first.matches("^\\d+$")) {
            String urls = ossService.selectUrlByIds(first);
            if (StringUtils.isBlank(urls)) {
                return null;
            }
            return urls.split(",")[0].trim();
        }
        return first;
    }

    private byte[] loadCompanySeal(Long companyId) {
        if (companyId == null) {
            return null;
        }
        SysCompanyVo company = sysCompanyService.queryById(companyId);
        if (company == null) {
            return null;
        }
        String seal = company.getSealUrl();
        if (StringUtils.isBlank(seal)) {
            return null;
        }
        String trimmed = seal.trim();
        if (trimmed.matches("^\\d+(,\\d+)*$")) {
            List<OssDTO> list = ossService.selectByIds(trimmed);
            if (list == null || list.isEmpty()) {
                return null;
            }
            String url = list.get(0).getUrl();
            return downloadBytes(url);
        } else {
            return downloadBytes(trimmed);
        }
    }

    private byte[] downloadBytes(String url) {
        if (StringUtils.isBlank(url)) {
            return null;
        }
        try {
            URL u = new URL(url);
            try (java.io.InputStream is = u.openStream()) {
                return is.readAllBytes();
            }
        } catch (Exception e) {
            log.error("Failed to download seal image from url: {}", url, e);
            return null;
        }
    }
}
