package com.kingdee.eas.custom.integration.apimgt.sync;

import com.kingdee.bos.BOSException;
import com.kingdee.bos.Context;
import com.kingdee.bos.util.BOSUuid;
import com.kingdee.eas.basedata.org.CtrlUnitInfo;
import com.kingdee.eas.custom.integration.apimgt.ApiRegistryBillCollection;
import com.kingdee.eas.custom.integration.apimgt.ApiRegistryBillFactory;
import com.kingdee.eas.custom.integration.apimgt.ApiRegistryBillInfo;
import com.kingdee.eas.custom.integration.apimgt.IApiRegistryBill;
import com.kingdee.eas.custom.integration.base.util.SHRApiUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;

/**
 * 服务启动时自动扫描 integration 目录下所有接口，同步写入 ApiRegistryBill 表。
 * 已存在的接口（按 number 字段判断）跳过，仅新增代码中注册但表中不存在的接口。
 *
 * <p>同步注解映射：</p>
 * <ul>
 *   <li>@SkipAuth → isAuthRequired</li>
 *   <li>@SkipCallLog → isSkipCallLog</li>
 *   <li>@ApiDoc.name → name</li>
 *   <li>@ApiDoc.desc → apiDesc</li>
 * </ul>
 *
 * <p>提供 {@link #rescan()} 方法供外部（如管理界面按钮）手动触发重新扫描。</p>
 */
public class ApiRegistrySyncRunner implements ApplicationListener<ContextRefreshedEvent> {

    private static final Logger logger = LoggerFactory.getLogger(ApiRegistrySyncRunner.class);

    private final RequestMappingHandlerMapping handlerMapping;

    private volatile boolean executed;

    public ApiRegistrySyncRunner(RequestMappingHandlerMapping handlerMapping) {
        this.handlerMapping = handlerMapping;
    }

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        if (executed) {
            return;
        }
        executed = true;

        try {
            doSyncInternal();
        } catch (Exception e) {
            logger.error("integration API 注册同步失败: {}", e.getMessage(), e);
        }
    }

    /**
     * 重新扫描并同步 API 注册，供外部（如管理界面按钮）调用。
     *
     * @return Map，包含 scanned / skip / new / error 四个计数
     */
    public synchronized Map<String, Integer> rescan() {
        logger.info("收到手动重新扫描 API 请求");
        return doSyncInternal();
    }

    private synchronized Map<String, Integer> doSyncInternal() {
        ApiScanner scanner = new ApiScanner();
        Map<String, ApiEndpoint> scannedApis = scanner.scan(handlerMapping);
        if (scannedApis.isEmpty()) {
            logger.info("integration API 扫描结果为空，跳过同步");
            return emptyResult();
        }

        Context ctx;
        try {
            ctx = SHRApiUtils.getContext();
        } catch (Exception e) {
            logger.warn("无法获取 BOS Context，跳过 API 注册同步: {}", e.getMessage());
            return emptyResult();
        }

        IApiRegistryBill iApiRegistryBill;
        try {
            iApiRegistryBill = ApiRegistryBillFactory.getLocalInstance(ctx);
        } catch (BOSException e) {
            logger.error("获取 ApiRegistryBill 实例失败: {}", e.getMessage(), e);
            return emptyResult();
        }

        // 加载已存在的接口记录
        Set<String> existingKeys = loadExistingKeys(iApiRegistryBill);
        logger.info("ApiRegistryBill 现有 {} 条记录", existingKeys.size());

        int newCount = 0;
        int skipCount = 0;
        int errorCount = 0;

        for (ApiEndpoint endpoint : scannedApis.values()) {
            String key = endpoint.buildKey();
            if (existingKeys.contains(key)) {
                skipCount++;
                continue;
            }

            try {
                ApiRegistryBillInfo info = new ApiRegistryBillInfo();
                // 设置编码
                info.setNumber(endpoint.getApiPath() + "#" + endpoint.getHttpMethod());
                // API 路径
                info.setApiPath(endpoint.getApiPath());
                // 接口名称（@ApiDoc.name 或回退 ClassName.methodName）
                info.setName(endpoint.getApiName());
                // HTTP 方法
                info.setHttpMethod(endpoint.getHttpMethod());
                // 接口描述（@ApiDoc.desc 或回退 ClassName.methodName）
                info.setApiDesc(endpoint.getApiDesc());
                // 是否跳过调用日志
                info.setIsSkipCallLog(endpoint.isSkipCallLog());
                // 是否需要认证
                info.setIsAuthRequired(endpoint.isAuthRequired());

                // 创建组织
                CtrlUnitInfo ctrlUnitInfo = new CtrlUnitInfo();
                ctrlUnitInfo.setId(BOSUuid.read("00000000-0000-0000-0000-000000000000CCE7AED4"));
                info.setCU(ctrlUnitInfo);

                iApiRegistryBill.save(info);
                newCount++;
                logger.info("新增接口注册: {}", key);
            } catch (Exception e) {
                errorCount++;
                logger.error("保存接口注册 {} 失败: {}", key, e.getMessage(), e);
            }
        }

        Map<String, Integer> result = new LinkedHashMap<>();
        result.put("scanned", scannedApis.size());
        result.put("skip", skipCount);
        result.put("new", newCount);
        result.put("error", errorCount);

        logger.info("integration API 注册同步完成: 扫描={}, 已存在={}, 新增={}, 失败={}",
                scannedApis.size(), skipCount, newCount, errorCount);
        return result;
    }

    private Map<String, Integer> emptyResult() {
        Map<String, Integer> result = new LinkedHashMap<>();
        result.put("scanned", 0);
        result.put("skip", 0);
        result.put("new", 0);
        result.put("error", 0);
        return result;
    }

    /**
     * 加载已存在的接口记录 key 集合。
     */
    private Set<String> loadExistingKeys(IApiRegistryBill iApiRegistryBill) {
        Set<String> keys = new HashSet<>();
        try {
            ApiRegistryBillCollection col = iApiRegistryBill.getApiRegistryBillCollection();
            if (col != null) {
                for (int i = 0; i < col.size(); i++) {
                    ApiRegistryBillInfo info = col.get(i);
                    keys.add(info.getNumber());
                }
            }
        } catch (BOSException e) {
            logger.warn("查询已注册接口列表失败，将尝试全部新增: {}", e.getMessage());
        }
        return keys;
    }
}
