package shop.alien.job.jobhandler;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import shop.alien.entity.result.R;
import shop.alien.entity.store.WechatPartnerApplyment;
import shop.alien.job.feign.AlienStoreFeign;
import shop.alien.mapper.WechatPartnerApplymentMapper;

import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * XXL-JOB：定时查询微信进件审核信息，并更新本地表 {@code wechat_partner_applyment}。
 * <p>
 * 查询方式：通过 {@link AlienStoreFeign}（OpenFeign）调用 alien-store 转发查询接口，由 alien-store 负责对微信签名请求+验签；\n
 * 本 Handler 解析返回数据，并按通过/驳回回写本地表。\n
 * </p>
 * <p>
 * 待处理记录<strong>仅从数据库查询</strong>，不通过 XXL-Job 任务参数传入申请单号；\n
 * 条数上限由配置 {@code job.wechatPartnerApplyment.batchSize} 控制。\n
 * </p>
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class WechatPartnerApplymentAuditSyncJobHandler {

    private final AlienStoreFeign alienStoreFeign;
    private final WechatPartnerApplymentMapper wechatPartnerApplymentMapper;

    /**
     * 【需配置】alien-store 根地址，与 {@link AlienStoreFeign} 的 {@code feign.alienStore.url} 一致；\n
     * 仅用于 XXL 日志展示，实际请求走 Feign。
     */
    @Value("${feign.alienStore.url:}")
    private String alienStoreFeignUrl;

    /** 默认批量大小 */
    @Value("${job.wechatPartnerApplyment.batchSize:1}")
    private int batchSize;

    /**
     * XXL-JOB 任务：同步进件审核状态（通过/驳回均更新）。
     * JobHandler 名称：{@code wechatPartnerApplymentAuditSync}
     */
    @XxlJob("wechatPartnerApplymentAuditSync")
    public ReturnT<String> wechatPartnerApplymentAuditSync(String param) {
        int shardIndex = XxlJobHelper.getShardIndex();
        int shardTotal = XxlJobHelper.getShardTotal();

        int limit = Math.max(1, Math.min(batchSize, 200));

        XxlJobHelper.log("[进件XXL] start shardIndex={}, shardTotal={}, limit={}, feign.alienStore.url={}",
                shardIndex, shardTotal, limit, alienStoreFeignUrl);

        // 仅从库查询待同步：未终态(0) 且已有 applyment_id；分片广播：按 applyment_id % shardTotal 分片
        List<WechatPartnerApplyment> list = wechatPartnerApplymentMapper.selectList(
                new LambdaQueryWrapper<WechatPartnerApplyment>()
                        .eq(WechatPartnerApplyment::getIsApproved, 0)
                        .isNotNull(WechatPartnerApplyment::getApplymentId)
                        .apply("MOD(applyment_id, {0}) = {1}", shardTotal, shardIndex)
                        .orderByAsc(WechatPartnerApplyment::getUpdatedAt)
                        .last("limit " + limit)
        );

        int ok = 0;
        int fail = 0;
        for (WechatPartnerApplyment r : list) {
            Long id = r.getApplymentId();
            if (id == null || id <= 0) {
                continue;
            }
            try {
                R<Map<String, Object>> result = alienStoreFeign.queryWechatPartnerApplymentState(id);
                if (!R.isSuccess(result)) {
                    fail++;
                    String msg = result != null ? result.getMsg() : "响应为空";
                    XxlJobHelper.log("[进件XXL] queryFail applyment_id={}, msg={}", id, msg);
                    continue;
                }
                Map<String, Object> dataMap = result != null ? result.getData() : null;
                if (dataMap == null || dataMap.isEmpty()) {
                    fail++;
                    XxlJobHelper.log("[进件XXL] queryFail applyment_id={}, data为空", id);
                    continue;
                }
                JSONObject data = JSON.parseObject(JSON.toJSONString(dataMap));
                String rawResp = JSON.toJSONString(result);
                // 回写本地表（通过/驳回/进行中都更新状态字段）
                upsertFromWechatQueryResult(r, data, rawResp);
                ok++;
                XxlJobHelper.log("[进件XXL] ok applyment_id={}, state={}", id, data.getString("applyment_state"));
            } catch (Exception e) {
                fail++;
                log.error("[进件XXL] Feign 查询异常 applyment_id={}", id, e);
                XxlJobHelper.log("[进件XXL] exception applyment_id={}, err={}", id, e.getMessage());
            }
        }

        XxlJobHelper.log("[进件XXL] done candidate={}, ok={}, fail={}", list.size(), ok, fail);
        return ReturnT.SUCCESS;
    }

    private void upsertFromWechatQueryResult(WechatPartnerApplyment existed, JSONObject data, String rawResp) {
        Date now = new Date();
        WechatPartnerApplyment record = existed != null ? existed : new WechatPartnerApplyment();
        record.setLastQueryTime(now);
        record.setLastQueryRespJson(rawResp);
        record.setUpdatedAt(now);

        record.setBusinessCode(blankToNull(data.getString("business_code")));
        record.setSubMchid(blankToNull(data.getString("sub_mchid")));
        record.setSignUrl(blankToNull(data.getString("sign_url")));
        record.setApplymentState(blankToNull(data.getString("applyment_state")));
        record.setApplymentStateMsg(blankToNull(data.getString("applyment_state_msg")));

        Integer approved = calcIsApproved(record.getApplymentState());
        record.setIsApproved(approved);
        if (approved != null && approved == 2) {
            record.setRejectReason(extractRejectReason(data));
        } else if (approved != null && approved == 1) {
            // 通过则清空驳回原因（避免旧值残留）
            record.setRejectReason(null);
        }

        // 仅更新当前行
        if (record.getId() != null) {
            wechatPartnerApplymentMapper.updateById(record);
        } else {
            // 兜底：按 applyment_id 找不到 id 的情况，插入一条（不建议频繁发生）
            record.setApplymentId(data.getLong("applyment_id"));
            if (record.getCreatedAt() == null) {
                record.setCreatedAt(now);
            }
            wechatPartnerApplymentMapper.insert(record);
        }
    }

    private static Integer calcIsApproved(String applymentState) {
        if (StringUtils.isBlank(applymentState)) {
            return 0;
        }
        if ("APPLYMENT_STATE_FINISHED".equalsIgnoreCase(applymentState)) {
            return 1;
        }
        if ("APPLYMENT_STATE_REJECTED".equalsIgnoreCase(applymentState)) {
            return 2;
        }
        return 0;
    }

    private static String extractRejectReason(JSONObject data) {
        try {
            if (data == null || data.getJSONArray("audit_detail") == null) {
                return null;
            }
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < data.getJSONArray("audit_detail").size(); i++) {
                JSONObject it = data.getJSONArray("audit_detail").getJSONObject(i);
                if (it == null) continue;
                String fieldName = it.getString("field_name");
                String field = it.getString("field");
                String reason = it.getString("reject_reason");
                if (StringUtils.isBlank(reason)) continue;
                if (sb.length() > 0) sb.append("；");
                String left = StringUtils.isNotBlank(fieldName) ? fieldName : (StringUtils.isNotBlank(field) ? field : "");
                if (StringUtils.isNotBlank(left)) sb.append(left).append("：");
                sb.append(reason);
            }
            String s = sb.toString();
            return s.length() <= 2048 ? s : s.substring(0, 2048);
        } catch (Exception e) {
            return null;
        }
    }

    private static String blankToNull(String s) {
        String t = s != null ? s.trim() : null;
        return StringUtils.isNotBlank(t) ? t : null;
    }
}

