package com.jeesharp.business.modules.coupon.application.support;

import cn.hutool.core.util.StrUtil;
import cn.binarywang.wx.miniapp.api.WxMaService;
import com.jeesharp.business.wechat.WeChatService;
import com.jeesharp.framework.exception.BizErrorException;
import com.jeesharp.framework.file.options.UploadOptions;
import com.jeesharp.framework.file.result.UploadResult;
import com.jeesharp.framework.file.service.FileStorageService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.ByteArrayInputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;

/**
 * 优惠券领券太阳码：调用微信「获取不限制的小程序码」并上传至文件存储。
 *
 * @author jeesharp
 * @since 2026-07-03
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class CouponTemplateWxQrcodeSupport {

    private static final String OBJECT_TYPE = "coupon_template_claim_qrcode";
    private static final String PNG_EXTENSION = ".png";
    private static final String JPG_EXTENSION = ".jpg";
    private static final String PNG_CONTENT_TYPE = "image/png";
    private static final String JPG_CONTENT_TYPE = "image/jpeg";

    private final WeChatService weChatService;
    private final FileStorageService fileStorageService;

    @Value("${coupon.wx-qrcode.page:package-views/pages/coupon/detail}")
    private String defaultPage;

    @Value("${coupon.wx-qrcode.env-version:release}")
    private String envVersion;

    @Value("${coupon.wx-qrcode.check-path:true}")
    private boolean checkPath;

    @Value("${coupon.wx-qrcode.width:430}")
    private int width;

    @Value("${coupon.wx-qrcode.bucket:images/coupon/claim-qrcode}")
    private String bucket;

    @Value("${coupon.wx-qrcode.storage-platform:}")
    private String storagePlatform;

    public String generateAndUpload(String wxScene, String templateId) {
        if (StrUtil.isBlank(wxScene)) {
            throw new BizErrorException("领券 scene 不能为空");
        }
        String page = normalizePage(defaultPage);
        byte[] imageBytes = createWxSunCode(wxScene.trim(), page);
        String imageExtension = resolveImageExtension(imageBytes);
        return uploadImage(imageBytes, wxScene.trim(), templateId, imageExtension);
    }

    public void writeImage(String fileKeyOrUrl, OutputStream outputStream) {
        if (StrUtil.isBlank(fileKeyOrUrl)) {
            throw new BizErrorException("领券码图片不存在");
        }
        fileStorageService.download(normalizeFileKey(fileKeyOrUrl), outputStream);
    }

    public static String normalizeDisplayUrl(String url) {
        if (StrUtil.isBlank(url)) {
            return url;
        }
        String normalized = url.trim().replace('\\', '/');
        while (normalized.contains("//") && !normalized.startsWith("http://") && !normalized.startsWith("https://")) {
            normalized = normalized.replace("//", "/");
        }
        return normalized;
    }

    public static String resolveImageContentType(String fileKeyOrUrl) {
        String normalized = normalizeDisplayUrl(fileKeyOrUrl);
        if (StrUtil.isNotBlank(normalized) && normalized.toLowerCase().contains(JPG_EXTENSION)) {
            return JPG_CONTENT_TYPE;
        }
        return PNG_CONTENT_TYPE;
    }

    private byte[] createWxSunCode(String scene, String page) {
        WxMaService wxMaService = weChatService.getWxMaService();
        try {
            return wxMaService.getQrcodeService().createWxaCodeUnlimitBytes(
                    scene,
                    page,
                    checkPath,
                    envVersion,
                    width,
                    true,
                    null,
                    false
            );
        } catch (WxErrorException e) {
            log.warn("优惠券太阳码生成失败 scene={} page={} errCode={} errMsg={}",
                    scene, page, e.getError().getErrorCode(), e.getError().getErrorMsg(), e);
            throw new BizErrorException("微信太阳码生成失败：" + e.getError().getErrorMsg());
        } catch (Exception e) {
            log.warn("优惠券太阳码生成异常 scene={} page={}", scene, page, e);
            throw new BizErrorException("微信太阳码生成失败，请稍后重试");
        }
    }

    private String uploadImage(byte[] imageBytes, String wxScene, String templateId, String imageExtension) {
        String safeScene = wxScene.replaceAll("[^a-zA-Z0-9_-]", "_");
        if (safeScene.length() > 24) {
            safeScene = safeScene.substring(0, 24);
        }
        String fileName = "coupon-claim-qrcode-" + safeScene + imageExtension;
        UploadOptions.Builder optionsBuilder = UploadOptions.builder()
                .objectType(OBJECT_TYPE)
                .objectId(templateId)
                .path(buildStoragePath(bucket));
        if (StrUtil.isNotBlank(storagePlatform)) {
            optionsBuilder.platform(storagePlatform.trim());
        }
        UploadOptions options = optionsBuilder.build();
        try (ByteArrayInputStream inputStream = new ByteArrayInputStream(imageBytes)) {
            UploadResult result = fileStorageService.uploadWithResult(inputStream, fileName, bucket, options);
            return resolveAccessibleUrl(result);
        } catch (BizErrorException e) {
            throw e;
        } catch (Exception e) {
            log.warn("领券太阳码上传失败 scene={}", wxScene, e);
            throw new BizErrorException("太阳码上传失败，请检查文件存储配置");
        }
    }

    private String resolveAccessibleUrl(UploadResult result) {
        String url = normalizeDisplayUrl(StrUtil.blankToDefault(result.getUrl(), result.getFileKey()));
        if (StrUtil.isNotBlank(url) && (url.startsWith("http://") || url.startsWith("https://"))) {
            return url;
        }
        String fileKey = normalizeFileKey(StrUtil.blankToDefault(result.getFileKey(), result.getPath()));
        if (StrUtil.isNotBlank(fileKey)) {
            String preview = normalizeDisplayUrl(fileStorageService.preview(fileKey));
            if (StrUtil.isNotBlank(preview) && (preview.startsWith("http://") || preview.startsWith("https://"))) {
                return preview;
            }
        }
        throw new BizErrorException("太阳码上传失败，未返回可访问的绝对地址");
    }

    private static String normalizeFileKey(String fileKeyOrUrl) {
        return normalizeDisplayUrl(fileKeyOrUrl);
    }

    private static String buildStoragePath(String bucketName) {
        String normalized = bucketName.trim().replace('\\', '/');
        while (normalized.startsWith("/")) {
            normalized = normalized.substring(1);
        }
        if (!normalized.endsWith("/")) {
            normalized = normalized + "/";
        }
        return normalized;
    }

    private static String resolveImageExtension(byte[] imageBytes) {
        if (imageBytes == null || imageBytes.length < 8) {
            throw new BizErrorException("微信太阳码生成失败：返回内容无效");
        }
        if (isPng(imageBytes)) {
            return PNG_EXTENSION;
        }
        if (isJpeg(imageBytes)) {
            return JPG_EXTENSION;
        }
        String snippet = new String(imageBytes, 0, Math.min(imageBytes.length, 200), StandardCharsets.UTF_8);
        throw new BizErrorException("微信太阳码生成失败，请检查 page 与 env-version 配置：" + snippet);
    }

    private static boolean isPng(byte[] imageBytes) {
        return imageBytes[0] == (byte) 0x89 && imageBytes[1] == 0x50
                && imageBytes[2] == 0x4E && imageBytes[3] == 0x47;
    }

    private static boolean isJpeg(byte[] imageBytes) {
        return imageBytes[0] == (byte) 0xFF && imageBytes[1] == (byte) 0xD8
                && imageBytes[2] == (byte) 0xFF;
    }

    private static String normalizePage(String pagePath) {
        String page = pagePath.trim();
        while (page.startsWith("/")) {
            page = page.substring(1);
        }
        return page;
    }
}
