package com.hotwj.platform.common.photoEvidence.service.impl;

import com.fasterxml.jackson.databind.JsonNode;
import com.hotwj.platform.common.photoEvidence.domain.HotPhotoEvidence;
import com.hotwj.platform.common.photoEvidence.domain.bo.PhotoEvidenceCreateBo;
import com.hotwj.platform.common.photoEvidence.domain.vo.PhotoEvidenceVo;
import com.hotwj.platform.common.photoEvidence.mapper.HotPhotoEvidenceMapper;
import com.hotwj.platform.common.photoEvidence.service.IPhotoEvidenceService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.enums.FormatsType;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.DateUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.json.utils.JsonUtils;
import org.dromara.common.oss.core.OssClient;
import org.dromara.common.oss.factory.OssFactory;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.system.domain.vo.SysOssVo;
import org.dromara.system.service.ISysOssService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Slf4j
@Service
public class PhotoEvidenceServiceImpl implements IPhotoEvidenceService {

    @Resource
    private HotPhotoEvidenceMapper hotPhotoEvidenceMapper;

    @Resource
    private ISysOssService ossService;

    @Value("${map.gd.key:}")
    private String gdKey;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public PhotoEvidenceVo create(PhotoEvidenceCreateBo bo) {
        Long originalOssId = parseOssId(bo.getOriginalOssId());
        SysOssVo originalOss = ossService.getById(originalOssId);
        if (originalOss == null) {
            throw new ServiceException("原始图片不存在");
        }

        Date captureTimeClient = new Date();
        Date captureTimeServer = captureTimeClient;

        String addressServer = reverseGeocode(bo.getLatitude(), bo.getLongitude());
        String addressSource;
        String addressForWatermark;
        if (StringUtils.isNotBlank(addressServer)) {
            addressSource = "SERVER";
            addressForWatermark = addressServer;
        } else {
            addressSource = "CLIENT";
            addressForWatermark = bo.getAddressTextClient();
        }

        Long watermarkedOssId = generateAndUploadWatermarkedImage(
                originalOss,
                captureTimeClient,
                addressForWatermark
        );
        String operatorName = bo.getOperatorName();
        if (StringUtils.isBlank(operatorName)) {
            operatorName = LoginHelper.getUsername();
        }

        HotPhotoEvidence entity = new HotPhotoEvidence();
        entity.setOriginalOssId(originalOssId);
        entity.setWatermarkedOssId(watermarkedOssId);
        entity.setCaptureTimeClient(captureTimeClient);
        entity.setCaptureTimeServer(captureTimeServer);
        entity.setLatitude(bo.getLatitude());
        entity.setLongitude(bo.getLongitude());
        entity.setAddressTextServer(addressServer);
        entity.setAddressTextClient(bo.getAddressTextClient());
        entity.setAddressSource(addressSource);
        entity.setBizType(bo.getBizType());
        entity.setBizId(bo.getBizId());
        entity.setCompanyId(bo.getCompanyId());
        entity.setOperatorId(bo.getOperatorId());
        entity.setOperatorName(operatorName);

        hotPhotoEvidenceMapper.insert(entity);

        PhotoEvidenceVo vo = new PhotoEvidenceVo();
        vo.setEvidenceId(entity.getId() != null ? String.valueOf(entity.getId()) : null);
        vo.setOriginalOssId(String.valueOf(originalOssId));
        vo.setWatermarkedOssId(watermarkedOssId != null ? String.valueOf(watermarkedOssId) : null);
        vo.setCaptureTimeClient(DateUtils.parseDateToStr(FormatsType.YYYY_MM_DD_HH_MM_SS, captureTimeClient));
        vo.setCaptureTimeServer(DateUtils.parseDateToStr(FormatsType.YYYY_MM_DD_HH_MM_SS, captureTimeServer));
        vo.setLatitude(bo.getLatitude());
        vo.setLongitude(bo.getLongitude());
        vo.setAddressTextServer(addressServer);
        vo.setAddressTextClient(bo.getAddressTextClient());
        vo.setAddressSource(addressSource);
        vo.setBizType(bo.getBizType());
        vo.setBizId(bo.getBizId());
        vo.setCompanyId(bo.getCompanyId());
        vo.setOperatorId(bo.getOperatorId());
        vo.setOperatorName(operatorName);

        return vo;
    }

    private Long parseOssId(String ossIdStr) {
        try {
            return Long.valueOf(ossIdStr);
        } catch (NumberFormatException e) {
            throw new ServiceException("originalOssId格式不正确");
        }
    }

    private Long generateAndUploadWatermarkedImage(SysOssVo originalOss,
                                                   Date captureTimeClient,
                                                   String address) {
        byte[] originalBytes = downloadOssFile(originalOss);
        BufferedImage originalImage = readImage(originalBytes);
        BufferedImage watermarked = addWatermark(originalImage, captureTimeClient, address);

        File tempFile = null;
        try {
            String suffix = originalOss.getFileSuffix();
            if (StringUtils.isBlank(suffix)) {
                suffix = "jpg";
            }
            if (suffix.startsWith(".")) {
                suffix = suffix.substring(1);
            }
            tempFile = File.createTempFile("photo_evidence_", "." + suffix);
            try (OutputStream os = new FileOutputStream(tempFile)) {
                ImageIO.write(watermarked, suffix, os);
            }
            return ossService.upload(tempFile).getOssId();
        } catch (IOException e) {
            throw new ServiceException("水印图片生成失败");
        } finally {
            if (tempFile != null && tempFile.exists()) {
                if (!tempFile.delete()) {
                    log.warn("临时文件删除失败: {}", tempFile.getAbsolutePath());
                }
            }
        }
    }

    private byte[] downloadOssFile(SysOssVo oss) {
        OssClient storage = OssFactory.instance(oss.getService());
        try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
            storage.download(oss.getFileName(), baos, null);
            return baos.toByteArray();
        } catch (Exception e) {
            throw new ServiceException("原始图片下载失败");
        }
    }

    private BufferedImage readImage(byte[] bytes) {
        try (InputStream is = new ByteArrayInputStream(bytes)) {
            BufferedImage image = ImageIO.read(is);
            if (image == null) {
                String hex = "";
                if (bytes.length >= 4) {
                    hex = String.format("%02X%02X%02X%02X", bytes[0], bytes[1], bytes[2], bytes[3]);
                }
                log.error("ImageIO.read returned null. Header: {}", hex);
                throw new ServiceException("原始图片格式不支持 (Header: " + hex + ")");
            }
            return image;
        } catch (IOException e) {
            log.error("图片解析失败", e);
            throw new ServiceException("原始图片解析失败: " + e.getMessage());
        }
    }

    private BufferedImage addWatermark(BufferedImage original,
                                       Date captureTimeClient,
                                       String address) {
        int width = original.getWidth();
        int height = original.getHeight();

        // 强制最小画布宽度，防止文字区域过窄
        int minWidth = 600;
        if (width < minWidth) {
            int newWidth = minWidth;
            int newHeight = (int) Math.round((double) height * newWidth / width);
            BufferedImage resized = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_RGB);
            Graphics2D gResize = resized.createGraphics();
            gResize.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
            gResize.drawImage(original, 0, 0, newWidth, newHeight, null);
            gResize.dispose();
            original = resized;
            width = newWidth;
            height = newHeight;
        }

        // 字体大小随宽度自适应（设置最小值）
        int fontSize = Math.max(20, width / 30);
        int padding = Math.max(fontSize, 16);

        String timeText = DateUtils.parseDateToStr(FormatsType.YYYY_MM_DD_HH_MM_SS, captureTimeClient);
        String addressText = "地址：" + (address != null ? address : "");

        // 构建字体并测量
        Font font;
        try (InputStream is = getClass().getResourceAsStream("/fonts/simsun.ttc")) {
            if (is == null) {
                font = new Font("SansSerif", Font.PLAIN, fontSize);
            } else {
                font = Font.createFont(Font.TRUETYPE_FONT, is).deriveFont(Font.PLAIN, fontSize);
            }
        } catch (Exception e) {
            log.warn("Failed to load simsun font, fallback to SansSerif", e);
            font = new Font("SansSerif", Font.PLAIN, fontSize);
        }

        // 使用临时图形上下文计算文字宽度与行高
        BufferedImage tmp = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
        Graphics2D gMeasure = tmp.createGraphics();
        gMeasure.setFont(font);
        FontMetrics fm = gMeasure.getFontMetrics();
        int contentWidth = Math.max(1, width - padding * 2);

        // 构造文本行：第一行时间，后续为地址按宽度换行
        List<String> lines = new ArrayList<>();
        lines.add(timeText);
        if (fm.stringWidth(addressText) <= contentWidth) {
            lines.add(addressText);
        } else {
            StringBuilder current = new StringBuilder();
            for (char c : addressText.toCharArray()) {
                if (fm.stringWidth(current.toString() + c) > contentWidth) {
                    lines.add(current.toString());
                    current = new StringBuilder();
                }
                current.append(c);
            }
            if (current.length() > 0) {
                lines.add(current.toString());
            }
        }
        int lineHeight = fm.getHeight() + fontSize / 4;
        gMeasure.dispose();

        // 计算白条高度，确保所有行完整显示
        int barHeight = Math.max(lineHeight + padding * 2, lines.size() * lineHeight + padding * 2);

        // 输出结果图
        BufferedImage result = new BufferedImage(width, height + barHeight, BufferedImage.TYPE_INT_RGB);
        Graphics2D g = result.createGraphics();
        try {
            // 绘制原图
            g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
            g.drawImage(original, 0, 0, null);

            // 绘制底部白条
            g.setColor(Color.WHITE);
            g.fillRect(0, height, width, barHeight);

            // 绘制文本
            g.setFont(font);
            g.setColor(new Color(51, 51, 51));
            g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);

            int y = height + padding + fm.getAscent();
            for (String line : lines) {
                g.drawString(line, padding, y);
                y += lineHeight;
            }
            return result;
        } finally {
            g.dispose();
        }
    }

    private String reverseGeocode(Double latitude, Double longitude) {
        if (latitude == null || longitude == null) {
            return null;
        }
        if (StringUtils.isBlank(gdKey)) {
            return null;
        }
        String location = longitude + "," + latitude;
        String url = "https://restapi.amap.com/v3/geocode/regeo?output=JSON&location="
                + location + "&key=" + gdKey + "&radius=1000&extensions=base";
        HttpURLConnection connection = null;
        try {
            URL u = new URL(url);
            connection = (HttpURLConnection) u.openConnection();
            connection.setConnectTimeout(3000);
            connection.setReadTimeout(3000);
            connection.setRequestMethod("GET");
            int code = connection.getResponseCode();
            if (code != HttpURLConnection.HTTP_OK) {
                return null;
            }
            try (InputStream is = connection.getInputStream()) {
                String body = new String(is.readAllBytes(), StandardCharsets.UTF_8);
                JsonNode root = JsonUtils.getObjectMapper().readTree(body);
                if (root == null) {
                    return null;
                }
                if (!"1".equals(root.path("status").asText())) {
                    return null;
                }
                JsonNode regeocode = root.path("regeocode");
                String formatted = regeocode.path("formatted_address").asText();
                if (StringUtils.isNotBlank(formatted)) {
                    return formatted;
                }
                return null;
            }
        } catch (Exception e) {
            log.warn("高德逆地理解析失败: {},{}", latitude, longitude);
            return null;
        } finally {
            if (connection != null) {
                connection.disconnect();
            }
        }
    }
}

