package org.hzhub.workflow.service.impl;

import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.hzhub.common.core.exception.ServiceException;
import org.hzhub.common.mybatis.core.page.PageQuery;
import org.hzhub.common.mybatis.core.page.TableDataInfo;
import org.hzhub.common.satoken.utils.LoginHelper;
import org.hzhub.system.domain.SysUser;
import org.hzhub.system.mapper.SysUserMapper;
import org.hzhub.workflow.domain.WecomApproval;
import org.hzhub.workflow.domain.WecomApprovalSyncLog;
import org.hzhub.workflow.domain.WecomApprovalTemplate;
import org.hzhub.workflow.domain.bo.WecomApprovalSubmitBo;
import org.hzhub.workflow.domain.vo.WecomApprovalVo;
import org.hzhub.workflow.mapper.WecomApprovalMapper;
import org.hzhub.workflow.mapper.WecomApprovalSyncLogMapper;
import org.hzhub.workflow.mapper.WecomApprovalTemplateMapper;
import org.hzhub.workflow.service.IWecomTenantConfigService;
import org.hzhub.workflow.service.IWecomApprovalService;
import org.hzhub.workflow.wecom.WecomApiClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.util.concurrent.Executor;
import org.hzhub.common.tenant.helper.TenantHelper;

import java.util.*;
import java.util.stream.Collectors;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 企业微信审批服务实现
 *
 * @author hzhub
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class WecomApprovalServiceImpl implements IWecomApprovalService {

    private final WecomApiClient wecomApiClient;
    private final WecomApprovalMapper approvalMapper;
    private final WecomApprovalTemplateMapper templateMapper;
    private final WecomApprovalSyncLogMapper syncLogMapper;
    private final SysUserMapper userMapper;
    private final IWecomTenantConfigService tenantConfigService;

    @Autowired
    @Qualifier("wecomSyncExecutor")
    private Executor asyncExecutor;

    /** 状态映射 */
    private static final Map<Integer, String> STATUS_MAP = Map.of(
        1, "审批中", 2, "已通过", 3, "已驳回",
        4, "已撤销", 6, "通过后撤销", 7, "已删除", 10, "已支付"
    );

    @Override
    public TableDataInfo<WecomApprovalVo> listApprovals(String type, String status, String keyword, PageQuery pageQuery) {
        LambdaQueryWrapper<WecomApproval> wrapper = new LambdaQueryWrapper<>();

        // 按当前登录用户过滤（申请人 或 审批人）
        String[] userWeComIds = getCurrentUserWeComIds();
        if (userWeComIds != null) {
            wrapper.and(w -> {
                // 我是申请人
                w.in(WecomApproval::getApplyerUserid, Arrays.asList(userWeComIds));
                // 或我是审批人
                for (String uid : userWeComIds) {
                    w.or().like(WecomApproval::getApproverUserids, uid);
                }
            });
        }

        if (type != null && !type.isBlank()) {
            wrapper.eq(WecomApproval::getSpName, type);
        }
        if (status != null && !status.isBlank()) {
            wrapper.eq(WecomApproval::getSpStatus, Integer.parseInt(status));
        }
        if (keyword != null && !keyword.isBlank()) {
            wrapper.and(w -> w
                .like(WecomApproval::getSpNo, keyword)
                .or().like(WecomApproval::getSpName, keyword)
                .or().like(WecomApproval::getSummaryInfo, keyword)
            );
        }
        wrapper.orderByDesc(WecomApproval::getApplyTime);

        Page<WecomApprovalVo> result = approvalMapper.selectVoPage(pageQuery.build(), wrapper);

        // 填充状态文本
        result.getRecords().forEach(vo -> vo.setSpStatusText(STATUS_MAP.getOrDefault(vo.getSpStatus(), "未知")));

        return TableDataInfo.build(result);
    }

    /**
     * 获取当前用户的 WeCom 身份标识（wecomUserid 和 openId）
     * 返回数组: [wecomUserid, openId]，可能为 null
     */
    private String[] getCurrentUserWeComIds() {
        try {
            Long userId = LoginHelper.getUserId();
            SysUser user = userMapper.selectById(userId);
            if (user == null) return null;

            List<String> ids = new ArrayList<>();
            if (user.getWecomUserid() != null && !user.getWecomUserid().isEmpty()) {
                ids.add(user.getWecomUserid());
            }
            if (user.getOpenId() != null && !user.getOpenId().isEmpty()) {
                if (!ids.contains(user.getOpenId())) {
                    ids.add(user.getOpenId());
                }
            }
            return ids.isEmpty() ? null : ids.toArray(new String[0]);
        } catch (Exception e) {
            log.warn("获取当前用户WeCom身份失败", e);
            return null;
        }
    }

    @Override
    public String getApprovalDetail(String spNo) {
        // 先查本地
        WecomApproval local = approvalMapper.selectOne(new LambdaQueryWrapper<WecomApproval>()
            .eq(WecomApproval::getSpNo, spNo));
        if (local != null) {
            // 返回详情，同时异步刷新
            asyncRefreshDetail(spNo);
        } else {
            // 本地没有，实时拉取
            syncApprovalDetail(spNo);
        }
        WecomApproval found = approvalMapper.selectOne(new LambdaQueryWrapper<WecomApproval>()
            .eq(WecomApproval::getSpNo, spNo));
        return JSONUtil.toJsonStr(found);
    }

    @Async
    public void asyncRefreshDetail(String spNo) {
        try { syncApprovalDetail(spNo); } catch (Exception e) { log.warn("异步刷新审批详情失败: {}", spNo, e); }
    }

    @Override
    public String submitApproval(WecomApprovalSubmitBo bo) {
        if (bo.getCreatorUserid() == null || bo.getCreatorUserid().isBlank()) {
            throw new ServiceException("申请人不能为空");
        }

        // 构建企业微信 API 请求体
        Map<String, Object> body = new LinkedHashMap<>();
        body.put("creator_userid", bo.getCreatorUserid());
        body.put("template_id", bo.getTemplateId());
        body.put("use_template_approver", bo.getUseTemplateApprover() != null ? bo.getUseTemplateApprover() : 1);

        if (bo.getUseTemplateApprover() != null && bo.getUseTemplateApprover() == 0 && bo.getApprover() != null) {
            List<Map<String, Object>> approvers = bo.getApprover().stream().map(a -> {
                Map<String, Object> m = new LinkedHashMap<>();
                m.put("attr", a.getAttr());
                m.put("userid", a.getUserid());
                return m;
            }).toList();
            body.put("approver", approvers);
        }

        if (bo.getNotifyer() != null && !bo.getNotifyer().isEmpty()) {
            body.put("notifyer", bo.getNotifyer().stream().map(u -> Map.of("userid", u)).toList());
        }
        if (bo.getNotifyType() != null) {
            body.put("notify_type", bo.getNotifyType());
        }

        // 表单数据
        List<Map<String, Object>> contents = bo.getContents().stream().map(c -> {
            Map<String, Object> m = new LinkedHashMap<>();
            m.put("control", c.getControl());
            m.put("id", c.getId());
            m.put("title", c.getTitle());
            m.put("value", c.getValue());
            return m;
        }).toList();
        body.put("apply_data", Map.of("contents", contents));

        // 摘要
        if (bo.getSummary() != null) {
            body.put("summary_list", List.of(Map.of("summary_info", List.of(Map.of("text", bo.getSummary())))));
        }

        String resp = wecomApiClient.submitApproval(JSONUtil.toJsonStr(body));
        var json = JSONUtil.parseObj(resp);
        int errcode = json.getInt("errcode", -1);
        if (errcode != 0) {
            throw new ServiceException("提交审批失败: " + json.getStr("errmsg"));
        }

        String spNo = json.getStr("sp_no");
        // 异步同步详情到本地
        asyncRefreshDetail(spNo);
        return spNo;
    }

    @Override
    public Long syncAllApprovalsAsync(int daysBack) {
        var config = tenantConfigService.getByCurrentTenant();
        if (config == null || !"1".equals(config.getEnabled())) {
            log.warn("企业微信未配置或未启用，跳过审批同步");
            throw new ServiceException("企业微信未配置或未启用，请先在「企业微信配置」中添加配置");
        }

        WecomApprovalSyncLog logEntry = new WecomApprovalSyncLog();
        logEntry.setSyncType("FULL");
        logEntry.setStatus("RUNNING");
        logEntry.setStartTime(new Date());
        logEntry.setAddedCount(0);
        logEntry.setUpdatedCount(0);
        logEntry.setErrorCount(0);
        logEntry.setOperator(LoginHelper.isLogin() ? LoginHelper.getUsername() : "system");
        syncLogMapper.insert(logEntry);

        // 捕获当前租户ID，在异步线程中恢复租户上下文
        String tenantId = TenantHelper.getTenantId();

        // 使用编程式异步执行（避免@Async自调用失效）
        asyncExecutor.execute(() -> {
            TenantHelper.dynamic(tenantId, () -> {
                try {
                    String result = doSyncApprovals(daysBack, logEntry);
                    logEntry.setEndTime(new Date());
                    logEntry.setDuration((int) ((logEntry.getEndTime().getTime() - logEntry.getStartTime().getTime()) / 1000));
                    logEntry.setStatus("COMPLETED");
                    syncLogMapper.updateById(logEntry);
                    log.info("异步全量同步完成: {}", result);
                } catch (Exception e) {
                    logEntry.setEndTime(new Date());
                    logEntry.setDuration((int) ((logEntry.getEndTime().getTime() - logEntry.getStartTime().getTime()) / 1000));
                    logEntry.setErrorMsg(e.getMessage());
                    logEntry.setStatus("FAILED");
                    syncLogMapper.updateById(logEntry);
                    log.error("异步全量同步失败", e);
                }
            });
        });

        return logEntry.getId();
    }

    @Override
    public String incrementalSync(int daysBack) {
        String syncType = "INCREMENTAL";
        return executeSync(daysBack, syncType);
    }

    @Override
    public String syncCurrentUserApprovals() {
        String[] weComIds = getCurrentUserWeComIds();
        if (weComIds == null || weComIds.length == 0) {
            return "当前用户未绑定企业微信账号，请先在管理后台执行用户同步";
        }

        String syncType = "MANUAL";
        return executeUserSync(weComIds, syncType);
    }

    /**
     * 执行同步（增量），记录同步日志
     */
    private String executeSync(int daysBack, String syncType) {
        var config = tenantConfigService.getByCurrentTenant();
        if (config == null || !"1".equals(config.getEnabled())) {
            log.warn("企业微信未配置或未启用，跳过审批同步");
            throw new ServiceException("企业微信未配置或未启用，请先在「企业微信配置」中添加配置");
        }

        WecomApprovalSyncLog logEntry = new WecomApprovalSyncLog();
        logEntry.setSyncType(syncType);
        logEntry.setStatus("RUNNING");
        logEntry.setStartTime(new Date());
        logEntry.setAddedCount(0);
        logEntry.setUpdatedCount(0);
        logEntry.setErrorCount(0);
        logEntry.setOperator(LoginHelper.isLogin() ? LoginHelper.getUsername() : "system");
        syncLogMapper.insert(logEntry);

        try {
            String result = doSyncApprovals(daysBack, logEntry);
            logEntry.setEndTime(new Date());
            logEntry.setDuration((int) ((logEntry.getEndTime().getTime() - logEntry.getStartTime().getTime()) / 1000));
            logEntry.setStatus("COMPLETED");
            syncLogMapper.updateById(logEntry);
            return result;
        } catch (Exception e) {
            logEntry.setEndTime(new Date());
            logEntry.setDuration((int) ((logEntry.getEndTime().getTime() - logEntry.getStartTime().getTime()) / 1000));
            logEntry.setErrorMsg(e.getMessage());
            logEntry.setStatus("FAILED");
            syncLogMapper.updateById(logEntry);
            throw e;
        }
    }

    /**
     * 执行用户同步（仅同步当前用户相关的审批）
     */
    private String executeUserSync(String[] weComIds, String syncType) {
        if (!isConfigured()) {
            return "企业微信未配置，无法同步";
        }

        WecomApprovalSyncLog logEntry = new WecomApprovalSyncLog();
        logEntry.setSyncType(syncType);
        logEntry.setStatus("RUNNING");
        logEntry.setStartTime(new Date());
        logEntry.setAddedCount(0);
        logEntry.setUpdatedCount(0);
        logEntry.setErrorCount(0);
        logEntry.setOperator(LoginHelper.getUsername());
        syncLogMapper.insert(logEntry);

        try {
            int daysBack = 1;
            long now = System.currentTimeMillis() / 1000;
            long start = now - (long) daysBack * 86400;

            String cursor = null;
            int synced = 0;
            int errors = 0;
            boolean hasMore = true;

            while (hasMore) {
                try {
                    // 过滤：只获取当前用户相关的审批
                    List<Map<String, Object>> filters = Arrays.stream(weComIds)
                        .map(id -> Map.<String, Object>of("key", "applyer", "value", List.of(id)))
                        .toList();

                    String resp = wecomApiClient.getApprovalInfo(start, now, cursor, 100, filters);
                    var json = JSONUtil.parseObj(resp);
                    int errcode = json.getInt("errcode", -1);
                    if (errcode != 0) {
                        log.error("获取审批单号失败: {}", json.getStr("errmsg"));
                        errors++;
                        break;
                    }

                    var spNoList = json.getJSONArray("sp_no_list");
                    if (spNoList == null || spNoList.isEmpty()) {
                        hasMore = false;
                        break;
                    }

                    for (int i = 0; i < spNoList.size(); i++) {
                        String spNo = spNoList.getStr(i);
                        try {
                            syncSingleDetail(spNo);
                            synced++;
                        } catch (Exception e) {
                            log.warn("同步审批详情失败: {}", spNo, e);
                            errors++;
                        }
                    }

                    String nextCursor = json.getStr("next_cursor");
                    if (nextCursor == null || nextCursor.isEmpty()) {
                        hasMore = false;
                    } else {
                        cursor = nextCursor;
                    }
                } catch (Exception e) {
                    log.error("同步审批数据异常", e);
                    errors++;
                    hasMore = false;
                }
            }

            logEntry.setAddedCount(synced);
            logEntry.setUpdatedCount(0);
            logEntry.setErrorCount(errors);
            logEntry.setEndTime(new Date());
            logEntry.setDuration((int) ((logEntry.getEndTime().getTime() - logEntry.getStartTime().getTime()) / 1000));
            logEntry.setStatus(errors > 0 ? "COMPLETED" : "COMPLETED");
            syncLogMapper.updateById(logEntry);

            return String.format("同步完成，共同步 %d 条，失败 %d 条", synced, errors);
        } catch (Exception e) {
            logEntry.setEndTime(new Date());
            logEntry.setDuration((int) ((logEntry.getEndTime().getTime() - logEntry.getStartTime().getTime()) / 1000));
            logEntry.setErrorMsg(e.getMessage());
            logEntry.setStatus("FAILED");
            syncLogMapper.updateById(logEntry);
            log.error("用户审批同步异常", e);
            return "同步异常: " + e.getMessage();
        }
    }

    private String doSyncApprovals(int daysBack, WecomApprovalSyncLog logEntry) {
        log.info("开始同步企业微信审批数据，范围: 近 {} 天", daysBack);
        long now = System.currentTimeMillis() / 1000;
        long totalStart = now - (long) daysBack * 86400;

        var tenantConfig = tenantConfigService.getByCurrentTenant();
        log.info("当前租户配置: corpid={}, 是否启用={}",
            tenantConfig != null ? tenantConfig.getCorpid() : "null",
            tenantConfig != null && "1".equals(tenantConfig.getEnabled()));

        int totalSynced = 0;
        int errors = 0;

        // 规则2: 时间跨度不能超过31天，需要分段拉取
        long maxSpanSeconds = 31L * 24 * 3600;
        long windowStart = totalStart;

        while (windowStart < now) {
            long windowEnd = Math.min(windowStart + maxSpanSeconds, now);
            log.info("分段拉取: {} ~ {}", new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(windowStart * 1000)),
                new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(windowEnd * 1000)));

            String cursor = null;
            boolean hasMore = true;
            while (hasMore) {
                try {
                    String resp = wecomApiClient.getApprovalInfo(windowStart, windowEnd, cursor, 100, null);
                    var json = JSONUtil.parseObj(resp);
                    int errcode = json.getInt("errcode", -1);
                    if (errcode != 0) {
                        String errMsg = json.getStr("errmsg");
                        log.error("获取审批单号失败: {}", errMsg);
                        logEntry.setErrorMsg(errMsg);
                        throw new ServiceException("同步失败: " + errMsg);
                    }

                    var spNoList = json.getJSONArray("sp_no_list");
                    log.info("WeCom API 返回 sp_no_list: {} 条, next_cursor: {}",
                        spNoList != null ? spNoList.size() : 0, json.getStr("next_cursor"));
                    if (spNoList == null || spNoList.isEmpty()) {
                        hasMore = false;
                        break;
                    }

                    // 逐个拉取详情
                    for (int i = 0; i < spNoList.size(); i++) {
                        String spNo = spNoList.getStr(i);
                        try {
                            syncSingleDetail(spNo);
                            totalSynced++;
                        } catch (Exception e) {
                            log.warn("同步审批详情失败: {}", spNo, e);
                            errors++;
                        }
                    }

                    String nextCursor = json.getStr("next_cursor");
                    if (nextCursor == null || nextCursor.isEmpty()) {
                        hasMore = false;
                    } else {
                        cursor = nextCursor;
                    }
                } catch (ServiceException e) {
                    throw e;
                } catch (Exception e) {
                    log.error("同步审批数据异常", e);
                    logEntry.setErrorMsg(e.getMessage());
                    throw new ServiceException("同步异常: " + e.getMessage());
                }
            }
            windowStart = windowEnd;
        }

        logEntry.setAddedCount(totalSynced);
        logEntry.setErrorCount(errors);

        String msg = String.format("企业微信审批数据同步完成，共同步 %d 条，失败 %d 条", totalSynced, errors);
        log.info(msg);
        return msg;
    }

    @Override
    public void syncApprovalDetail(String spNo) {
        syncSingleDetail(spNo);
    }

    private void syncSingleDetail(String spNo) {
        String resp = wecomApiClient.getApprovalDetail(spNo);
        var json = JSONUtil.parseObj(resp);
        int errcode = json.getInt("errcode", -1);
        if (errcode != 0) {
            throw new RuntimeException("获取审批详情失败: " + json.getStr("errmsg"));
        }

        var info = json.getJSONObject("info");
        WecomApproval approval = new WecomApproval();
        approval.setSpNo(info.getStr("sp_no"));
        approval.setSpName(info.getStr("sp_name"));
        approval.setSpStatus(info.getInt("sp_status"));
        approval.setTemplateId(info.getStr("template_id"));

        // 查询模板名称
        String templateId = info.getStr("template_id");
        if (templateId != null) {
            WecomApprovalTemplate template = templateMapper.selectOne(new LambdaQueryWrapper<WecomApprovalTemplate>()
                .eq(WecomApprovalTemplate::getTemplateId, templateId));
            if (template != null && template.getTemplateName() != null) {
                approval.setTemplateName(template.getTemplateName());
            }
        }

        approval.setApplyTime(info.getLong("apply_time"));

        var applyer = info.getJSONObject("applyer");
        if (applyer != null) {
            log.debug("申请人信息: {}", applyer.toString());
            approval.setApplyerUserid(applyer.getStr("userid"));
            // 企业微信API可能返回 party 字段或 partyid 字段
            String party = applyer.getStr("party");
            if (party == null || party.isEmpty()) {
                // 尝试从 partyid 字段获取（可能是部门ID）
                party = applyer.getStr("partyid");
                log.debug("申请人部门party为空，尝试partyid: {}", party);
            }
            approval.setApplyerParty(party);
            log.debug("设置申请人部门: {}", party);
        } else {
            log.warn("审批单 {} 无申请人信息", spNo);
        }

        approval.setApplyData(info.get("apply_data") != null ? info.get("apply_data").toString() : null);

        // 保存审批流程节点数据（process_list）
        approval.setNodesData(info.get("process_list") != null ? info.get("process_list").toString() : null);

        // 提取所有审批人userid（从 process_list.node_list 中提取）
        String approverUserids = extractApproverUserids(info);
        approval.setApproverUserids(approverUserids);

        // 提取摘要
        var summaryList = info.getJSONArray("summary_list");
        if (summaryList != null && !summaryList.isEmpty()) {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < summaryList.size(); i++) {
                var summaryItem = summaryList.getJSONObject(i);
                var summaryInfo = summaryItem.getJSONArray("summary_info");
                if (summaryInfo != null && !summaryInfo.isEmpty()) {
                    sb.append(summaryInfo.getJSONObject(0).getStr("text"));
                    if (i < summaryList.size() - 1) sb.append("; ");
                }
            }
            approval.setSummaryInfo(sb.toString());
        }

        // 提取金额（从 apply_data.contents 中找 Money 类型控件）
        BigDecimal amount = extractAmount(info);
        approval.setAmount(amount);

        approval.setSyncTime(new Date());

        // UPSERT
        WecomApproval existing = approvalMapper.selectOne(new LambdaQueryWrapper<WecomApproval>()
            .eq(WecomApproval::getSpNo, spNo));
        if (existing != null) {
            approval.setId(existing.getId());
            approvalMapper.updateById(approval);
        } else {
            approvalMapper.insert(approval);
        }
    }

    /**
     * 从审批数据中提取金额
     */
    private BigDecimal extractAmount(cn.hutool.json.JSONObject info) {
        try {
            var applyData = info.getJSONObject("apply_data");
            if (applyData == null) return BigDecimal.ZERO;
            var contents = applyData.getJSONArray("contents");
            if (contents == null) return BigDecimal.ZERO;

            for (int i = 0; i < contents.size(); i++) {
                var content = contents.getJSONObject(i);
                if ("Money".equals(content.getStr("control"))) {
                    var value = content.getJSONObject("value");
                    if (value != null && value.containsKey("new_num")) {
                        return value.getBigDecimal("new_num");
                    }
                }
            }
        } catch (Exception e) {
            log.debug("提取金额失败", e);
        }
        return BigDecimal.ZERO;
    }

    /**
     * 从审批数据中提取审批人userid
     * 企业微信审批详情中，审批人信息在 process_list.node_list[].sub_node_list[].userid 中
     */
    private String extractApproverUserids(cn.hutool.json.JSONObject info) {
        try {
            var processList = info.getJSONObject("process_list");
            if (processList == null) {
                log.warn("审批单无 process_list 数据");
                return null;
            }

            var nodeList = processList.getJSONArray("node_list");
            if (nodeList == null || nodeList.isEmpty()) {
                log.warn("审批单 process_list 中无 node_list 数据");
                return null;
            }

            var userids = new LinkedHashSet<String>();
            log.debug("开始提取审批人，node_list数量: {}", nodeList.size());

            for (int i = 0; i < nodeList.size(); i++) {
                var node = nodeList.getJSONObject(i);
                Integer nodeType = node.getInt("node_type");

                // node_type: 1=审批节点, 2=抄送节点
                // 只提取审批节点的审批人（node_type=1）
                if (nodeType != null && nodeType == 1) {
                    var subNodeList = node.getJSONArray("sub_node_list");
                    if (subNodeList != null) {
                        for (int j = 0; j < subNodeList.size(); j++) {
                            var subNode = subNodeList.getJSONObject(j);
                            var userid = subNode.getStr("userid");
                            if (userid != null && !userid.isEmpty()) {
                                userids.add(userid);
                                log.debug("提取到审批人userid: {}", userid);
                            }
                        }
                    }
                }
            }

            log.debug("提取的审批人列表: {}", userids);
            return userids.isEmpty() ? null : String.join(",", userids);
        } catch (Exception e) {
            log.warn("提取审批人userid失败", e);
        }
        return null;
    }

    @Override
    public List<Map<String, Object>> listTemplates() {
        return templateMapper.selectList(new LambdaQueryWrapper<WecomApprovalTemplate>())
            .stream()
            .map(t -> {
                Map<String, Object> m = new LinkedHashMap<>();
                m.put("templateId", t.getTemplateId());
                m.put("templateName", t.getTemplateName());
                m.put("syncTime", t.getSyncTime());
                return m;
            })
            .collect(Collectors.toList());
    }

    @Override
    public void syncTemplate(String templateId) {
        try {
            String resp = wecomApiClient.getTemplateDetail(templateId);
            log.info("模板详情API响应(templateId={}): {}", templateId, resp);
            var json = JSONUtil.parseObj(resp);
            int errcode = json.getInt("errcode", -1);
            if (errcode != 0) {
                log.warn("同步模板失败: {}", json.getStr("errmsg"));
                return;
            }

            WecomApprovalTemplate template = new WecomApprovalTemplate();
            template.setTemplateId(templateId);

            // 模板名称（从 template_names 数组中提取）
            var names = json.getJSONArray("template_names");
            if (names != null && !names.isEmpty()) {
                for (int i = 0; i < names.size(); i++) {
                    var nameItem = names.getJSONObject(i);
                    if ("zh_CN".equals(nameItem.getStr("lang"))) {
                        template.setTemplateName(nameItem.getStr("text"));
                        break;
                    }
                }
                if (template.getTemplateName() == null) {
                    template.setTemplateName(names.getJSONObject(0).getStr("text"));
                }
            }

            log.info("提取的模板名称: {}", template.getTemplateName());

            template.setTemplateConfig(json.get("template_content") != null ? json.get("template_content").toString() : null);
            template.setSyncTime(new Date());

            WecomApprovalTemplate existing = templateMapper.selectOne(new LambdaQueryWrapper<WecomApprovalTemplate>()
                .eq(WecomApprovalTemplate::getTemplateId, templateId));
            if (existing != null) {
                template.setId(existing.getId());
                templateMapper.updateById(template);
            } else {
                templateMapper.insert(template);
            }
        } catch (Exception e) {
            log.error("同步模板异常: {}", templateId, e);
        }
    }

    @Override
    public Map<String, Long> getStats() {
        Map<String, Long> stats = new LinkedHashMap<>();
        stats.put("pending", approvalMapper.selectCount(new LambdaQueryWrapper<WecomApproval>()
            .eq(WecomApproval::getSpStatus, 1)));
        stats.put("approved", approvalMapper.selectCount(new LambdaQueryWrapper<WecomApproval>()
            .eq(WecomApproval::getSpStatus, 2)));
        stats.put("rejected", approvalMapper.selectCount(new LambdaQueryWrapper<WecomApproval>()
            .eq(WecomApproval::getSpStatus, 3)));
        stats.put("revoked", approvalMapper.selectCount(new LambdaQueryWrapper<WecomApproval>()
            .eq(WecomApproval::getSpStatus, 4)));
        stats.put("total", approvalMapper.selectCount(null));
        return stats;
    }

    @Override
    public Map<String, Long> getUserStats() {
        String[] userWeComIds = getCurrentUserWeComIds();
        Map<String, Long> stats = new LinkedHashMap<>();
        if (userWeComIds == null) {
            stats.put("pending", 0L);
            stats.put("approved", 0L);
            stats.put("rejected", 0L);
            stats.put("revoked", 0L);
            stats.put("total", 0L);
            stats.put("mine", 0L);
            return stats;
        }
        stats.put("pending", approvalMapper.selectCount(new LambdaQueryWrapper<WecomApproval>()
            .in(WecomApproval::getApplyerUserid, Arrays.asList(userWeComIds))
            .eq(WecomApproval::getSpStatus, 1)));
        stats.put("approved", approvalMapper.selectCount(new LambdaQueryWrapper<WecomApproval>()
            .in(WecomApproval::getApplyerUserid, Arrays.asList(userWeComIds))
            .eq(WecomApproval::getSpStatus, 2)));
        stats.put("rejected", approvalMapper.selectCount(new LambdaQueryWrapper<WecomApproval>()
            .in(WecomApproval::getApplyerUserid, Arrays.asList(userWeComIds))
            .eq(WecomApproval::getSpStatus, 3)));
        stats.put("revoked", approvalMapper.selectCount(new LambdaQueryWrapper<WecomApproval>()
            .in(WecomApproval::getApplyerUserid, Arrays.asList(userWeComIds))
            .eq(WecomApproval::getSpStatus, 4)));
        stats.put("total", approvalMapper.selectCount(new LambdaQueryWrapper<WecomApproval>()
            .in(WecomApproval::getApplyerUserid, Arrays.asList(userWeComIds))));
        stats.put("mine", stats.get("total"));
        return stats;
    }

    @Override
    public TableDataInfo<Map<String, Object>> listSyncLogs(PageQuery pageQuery) {
        LambdaQueryWrapper<WecomApprovalSyncLog> wrapper = new LambdaQueryWrapper<>();
        wrapper.orderByDesc(WecomApprovalSyncLog::getStartTime);

        Page<WecomApprovalSyncLog> page = syncLogMapper.selectPage(pageQuery.build(), wrapper);

        // 转换为 Map 方便前端使用
        List<Map<String, Object>> records = page.getRecords().stream().map(log -> {
            Map<String, Object> m = new LinkedHashMap<>();
            m.put("id", log.getId());
            m.put("syncType", log.getSyncType());
            m.put("syncTypeText", switch (log.getSyncType()) {
                case "FULL" -> "全量同步";
                case "INCREMENTAL" -> "增量同步";
                case "MANUAL" -> "手动同步";
                default -> log.getSyncType();
            });
            m.put("status", log.getStatus());
            m.put("statusText", switch (log.getStatus() != null ? log.getStatus() : "") {
                case "RUNNING" -> "同步中";
                case "COMPLETED" -> "已完成";
                case "FAILED" -> "失败";
                default -> "未知";
            });
            m.put("startTime", log.getStartTime());
            m.put("endTime", log.getEndTime());
            m.put("duration", log.getDuration());
            m.put("addedCount", log.getAddedCount());
            m.put("updatedCount", log.getUpdatedCount());
            m.put("errorCount", log.getErrorCount());
            m.put("errorMsg", log.getErrorMsg());
            m.put("operator", log.getOperator());
            return m;
        }).toList();

        TableDataInfo<Map<String, Object>> result = TableDataInfo.build();
        result.setRows(records);
        result.setTotal(page.getTotal());
        return result;
    }

    @Override
    public void syncAllTemplates() {
        log.info("开始同步企业微信审批模板");
        // 从已同步的审批数据中提取所有 template_id
        List<String> templateIds = approvalMapper.selectList(
            new LambdaQueryWrapper<WecomApproval>()
                .isNotNull(WecomApproval::getTemplateId)
                .select(WecomApproval::getTemplateId)
        ).stream()
            .map(WecomApproval::getTemplateId)
            .distinct()
            .collect(Collectors.toList());

        log.info("从审批数据中发现 {} 个模板ID", templateIds.size());

        int synced = 0;
        int errors = 0;
        for (String templateId : templateIds) {
            try {
                syncTemplate(templateId);
                synced++;
            } catch (Exception e) {
                log.warn("同步模板失败: {}", templateId, e);
                errors++;
            }
        }
        log.info("模板同步完成: 成功={}, 失败={}", synced, errors);
    }

    private boolean isConfigured() {
        var config = tenantConfigService.getByCurrentTenant();
        return config != null && "1".equals(config.getEnabled());
    }
}
