package com.jeesharp.business.modules.contentsecurity.application.provider;

import cn.binarywang.wx.miniapp.api.WxMaSecurityService;
import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaMediaAsyncCheckResult;
import cn.binarywang.wx.miniapp.bean.security.WxMaMediaSecCheckCheckRequest;
import cn.binarywang.wx.miniapp.bean.security.WxMaMsgSecCheckCheckRequest;
import cn.binarywang.wx.miniapp.bean.security.WxMaMsgSecCheckCheckResponse;
import cn.binarywang.wx.miniapp.constant.WxMaConstants;
import cn.hutool.core.util.StrUtil;
import com.jeesharp.business.modules.contentsecurity.api.spi.ContentSecurityProvider;
import com.jeesharp.business.modules.contentsecurity.config.ContentSecurityProperties;
import com.jeesharp.business.modules.contentsecurity.dto.ContentCheckRequest;
import com.jeesharp.business.modules.contentsecurity.dto.ContentCheckResult;
import com.jeesharp.business.modules.contentsecurity.enums.CheckDecisionEnum;
import com.jeesharp.business.modules.contentsecurity.enums.ContentSecurityProviderCodeEnum;
import com.jeesharp.business.modules.contentsecurity.enums.ContentTypeEnum;
import com.jeesharp.business.wechat.WeChatService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import org.springframework.stereotype.Component;

/**
 * 微信小程序内容安全 Provider：msgSecCheck v2 与 mediaCheckAsync / img_sec_check。
 *
 * @author jeesharp
 * @since 2026-06-27
 */
@Slf4j
@Component
@AllArgsConstructor
public class WeChatMiniContentSecurityProvider implements ContentSecurityProvider {

    private static final String VERSION_V2 = "2";
    private static final String SUGGEST_PASS = "pass";

    private final WeChatService weChatService;
    private final ContentSecurityProperties properties;

    @Override
    public String providerCode() {
        return ContentSecurityProviderCodeEnum.WECHAT_MINI.getCode();
    }

    @Override
    public boolean supports(ContentTypeEnum contentType) {
        return contentType == ContentTypeEnum.TEXT || contentType == ContentTypeEnum.IMAGE;
    }

    @Override
    public ContentCheckResult checkText(ContentCheckRequest request) {
        if (StrUtil.isBlank(request.getText())) {
            return passResult(null, false);
        }
        if (StrUtil.isBlank(request.getOpenid())) {
            return ContentCheckResult.builder()
                    .decision(CheckDecisionEnum.REVIEW)
                    .providerCode(providerCode())
                    .rejectReason("缺少微信身份，内容将进入人工审核")
                    .resultLabel("openid_missing")
                    .async(false)
                    .build();
        }
        try {
            WxMaService wxMaService = weChatService.getWxMaService();
            WxMaSecurityService securityService = wxMaService.getSecurityService();
            int scene = request.getScene() != null ? request.getScene().getWechatScene() : 2;
            WxMaMsgSecCheckCheckRequest wxReq = WxMaMsgSecCheckCheckRequest.builder()
                    .version(VERSION_V2)
                    .openid(request.getOpenid())
                    .scene(scene)
                    .content(request.getText())
                    .build();
            WxMaMsgSecCheckCheckResponse wxResp = securityService.checkMessage(wxReq);
            CheckDecisionEnum decision = CheckDecisionEnum.fromWechatSuggest(
                    wxResp.getResult() != null ? wxResp.getResult().getSuggest() : SUGGEST_PASS);
            String label = wxResp.getResult() != null ? wxResp.getResult().getLabel() : null;
            return ContentCheckResult.builder()
                    .decision(decision)
                    .providerCode(providerCode())
                    .traceId(wxResp.getTraceId())
                    .rejectReason(decision == CheckDecisionEnum.REJECT ? "内容不符合规范，请修改后重试" : null)
                    .resultLabel(label)
                    .async(false)
                    .build();
        } catch (WxErrorException e) {
            log.warn("微信文本内容安全检测异常 errCode={} msg={}", e.getError().getErrorCode(), e.getError().getErrorMsg());
            if (isContentViolation(e)) {
                return ContentCheckResult.builder()
                        .decision(CheckDecisionEnum.REJECT)
                        .providerCode(providerCode())
                        .rejectReason("内容不符合规范，请修改后重试")
                        .resultLabel(String.valueOf(e.getError().getErrorCode()))
                        .async(false)
                        .build();
            }
            throw new IllegalStateException("微信内容安全接口调用失败: " + e.getError().getErrorMsg(), e);
        }
    }

    @Override
    public ContentCheckResult checkMedia(ContentCheckRequest request) {
        if (StrUtil.isBlank(request.getMediaUrl())) {
            return passResult(null, false);
        }
        if (properties.isMediaAsyncEnabled()) {
            ContentCheckResult asyncResult = checkMediaAsync(request);
            if (asyncResult != null) {
                return asyncResult;
            }
        }
        return checkMediaSync(request);
    }

    private ContentCheckResult checkMediaAsync(ContentCheckRequest request) {
        if (StrUtil.isBlank(request.getOpenid())) {
            if (properties.isMediaAsyncFallbackSync()) {
                return null;
            }
            return ContentCheckResult.builder()
                    .decision(CheckDecisionEnum.REVIEW)
                    .providerCode(providerCode())
                    .resultLabel("openid_missing")
                    .async(false)
                    .build();
        }
        try {
            WxMaService wxMaService = weChatService.getWxMaService();
            WxMaSecurityService securityService = wxMaService.getSecurityService();
            int scene = request.getScene() != null ? request.getScene().getWechatScene() : 2;
            WxMaMediaSecCheckCheckRequest wxReq = WxMaMediaSecCheckCheckRequest.builder()
                    .mediaUrl(request.getMediaUrl())
                    .mediaType(WxMaConstants.SecCheckMediaType.IMAGE)
                    .version(2)
                    .openid(request.getOpenid())
                    .scene(scene)
                    .build();
            WxMaMediaAsyncCheckResult asyncResult = securityService.mediaCheckAsync(wxReq);
            String traceId = asyncResult != null ? asyncResult.getTraceId() : null;
            if (StrUtil.isBlank(traceId)) {
                return properties.isMediaAsyncFallbackSync() ? null : pendingResult(null);
            }
            return pendingResult(traceId);
        } catch (WxErrorException e) {
            log.warn("微信异步图片检测提交失败 url={} err={}", request.getMediaUrl(), e.getError().getErrorMsg());
            if (properties.isMediaAsyncFallbackSync()) {
                return null;
            }
            throw new IllegalStateException("微信图片异步检测提交失败: " + e.getError().getErrorMsg(), e);
        }
    }

    private ContentCheckResult checkMediaSync(ContentCheckRequest request) {
        try {
            WxMaService wxMaService = weChatService.getWxMaService();
            WxMaSecurityService securityService = wxMaService.getSecurityService();
            securityService.checkImage(request.getMediaUrl());
            return passResult(null, false);
        } catch (WxErrorException e) {
            log.warn("微信图片内容安全检测未通过 url={} errCode={}", request.getMediaUrl(), e.getError().getErrorCode());
            if (isContentViolation(e)) {
                return ContentCheckResult.builder()
                        .decision(CheckDecisionEnum.REJECT)
                        .providerCode(providerCode())
                        .rejectReason("图片不符合规范，请更换后重试")
                        .resultLabel(String.valueOf(e.getError().getErrorCode()))
                        .async(false)
                        .build();
            }
            throw new IllegalStateException("微信图片安全接口调用失败: " + e.getError().getErrorMsg(), e);
        }
    }

    private static ContentCheckResult pendingResult(String traceId) {
        return ContentCheckResult.builder()
                .decision(CheckDecisionEnum.PENDING)
                .providerCode(ContentSecurityProviderCodeEnum.WECHAT_MINI.getCode())
                .traceId(traceId)
                .async(true)
                .pendingTraceIds(traceId != null ? java.util.List.of(traceId) : java.util.List.of())
                .build();
    }

    private static ContentCheckResult passResult(String traceId, boolean async) {
        return ContentCheckResult.builder()
                .decision(CheckDecisionEnum.PASS)
                .providerCode(ContentSecurityProviderCodeEnum.WECHAT_MINI.getCode())
                .traceId(traceId)
                .async(async)
                .build();
    }

    private static boolean isContentViolation(WxErrorException e) {
        int code = e.getError().getErrorCode();
        return code == 87014 || code == 87015;
    }
}
