package com.huangzj.base.spring;

import com.huangzj.base.cmd.Cmd;
import com.huangzj.base.cmd.CommandSource;
import com.huangzj.base.cmd.GameCommand;
import com.huangzj.base.cmd.GameCommandLookup;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.util.ClassUtils;

/**
 * 启动时把 Spring 容器里的 {@link GameCommand} 扫一遍，按 {@link Cmd} 过滤来源。
 * 当前 key 为 {@link Cmd#serviceId()} + {@link Cmd#methodId()}（与 {@link com.huangzj.cmd.gen.CmdRpcEnvelope} 一致）。
 */
@Slf4j
public class SpringGameCommandRegistry implements GameCommandLookup {

    private final Map<String, GameCommand> commands = new HashMap<>();

    /** 只收某一种来源的命令。 */
    public SpringGameCommandRegistry(ApplicationContext applicationContext, CommandSource source) {
        this(applicationContext, EnumSet.of(source));
    }

    /** 区服若收多种来源，用 Set 传多个 {@link CommandSource}。 */
    public SpringGameCommandRegistry(ApplicationContext applicationContext, Set<CommandSource> sources) {
        Map<String, GameCommand> beans = applicationContext.getBeansOfType(GameCommand.class);
        for (GameCommand cmd : beans.values()) {
            Class<?> userClass = ClassUtils.getUserClass(cmd);
            Cmd meta = userClass.getAnnotation(Cmd.class);
            // 同一 Bean 可能带 @Cmd(source=Client_TCP)，在跨服进程扫描时会被 sources 过滤掉
            if (meta != null && sources.contains(meta.source())) {
                commands.put(key(meta.serviceId(), meta.methodId()), cmd);
            }
        }
        log.info("GameCommand registry sources={} size={} keys={}", sources, commands.size(), commands.keySet());
    }

    private static String key(int serviceId, int methodId) {
        return ":" + serviceId + ":" + methodId;
    }

    @Override
    public GameCommand resolve(int serviceId, int methodId) {
        GameCommand cmd = commands.get(key(serviceId, methodId));
        if (cmd == null) {
            throw new IllegalArgumentException("unknown " + serviceId + "/" + methodId);
        }
        return cmd;
    }
}
