package tech.easyflow.agent.service.impl;

import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tech.easyflow.agent.entity.Agent;
import tech.easyflow.agent.entity.AgentSkillBinding;
import tech.easyflow.agent.mapper.AgentMapper;
import tech.easyflow.agent.mapper.AgentSkillBindingMapper;
import tech.easyflow.agent.runtime.skill.AgentSkillRuntimeProjector;
import tech.easyflow.agent.service.AgentSkillBindingService;
import tech.easyflow.agent.support.AgentBindingLockExecutor;
import tech.easyflow.common.entity.LoginAccount;
import tech.easyflow.common.satoken.util.SaTokenUtil;
import tech.easyflow.common.web.exceptions.BusinessException;
import tech.easyflow.skill.entity.Skill;
import tech.easyflow.skill.service.SkillService;
import tech.easyflow.system.enums.CategoryResourceType;
import tech.easyflow.system.enums.ResourceAction;
import tech.easyflow.system.service.ResourceAccessService;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Agent Skill 绑定服务实现。
 */
@Service
public class AgentSkillBindingServiceImpl
        extends ServiceImpl<AgentSkillBindingMapper, AgentSkillBinding>
        implements AgentSkillBindingService {

    private final AgentMapper agentMapper;
    private final AgentBindingLockExecutor bindingLockExecutor;
    private final AgentSkillRuntimeProjector runtimeProjector;
    private final SkillService skillService;
    private final ResourceAccessService resourceAccessService;

    /**
     * 创建 Agent Skill 绑定服务。
     *
     * @param agentMapper Agent Mapper
     * @param bindingLockExecutor Agent 绑定锁执行器
     * @param runtimeProjector Skill 运行投影器
     * @param skillService Skill 服务
     * @param resourceAccessService 资源权限服务
     */
    public AgentSkillBindingServiceImpl(AgentMapper agentMapper,
                                        AgentBindingLockExecutor bindingLockExecutor,
                                        AgentSkillRuntimeProjector runtimeProjector,
                                        SkillService skillService,
                                        ResourceAccessService resourceAccessService) {
        this.agentMapper = agentMapper;
        this.bindingLockExecutor = bindingLockExecutor;
        this.runtimeProjector = runtimeProjector;
        this.skillService = skillService;
        this.resourceAccessService = resourceAccessService;
    }

    /** {@inheritDoc} */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<AgentSkillBinding> replaceBindings(BigInteger agentId,
                                                   List<AgentSkillBinding> bindings) {
        return bindingLockExecutor.execute(agentId, () -> {
            Agent agent = requireAgentForUpdate(agentId);
            resourceAccessService.assertAccess(
                    CategoryResourceType.AGENT, agent, ResourceAction.MANAGE, "无权限管理该 Agent");
            List<AgentSkillBinding> current = listBindings(agentId);
            if (AgentBindingSemanticComparator.sameSkills(current, bindings)) {
                return listSummaries(agentId);
            }
            List<AgentSkillBinding> normalized = normalize(agent, bindings);
            // 在删除旧绑定前完成权限、包完整性、Tool 与 8 MiB 预算校验，失败时保留旧组。
            List<AgentSkillBinding> projected = runtimeProjector.projectCurrentBindings(agent, normalized);
            Map<BigInteger, Map<String, Object>> summaries = new HashMap<>();
            for (AgentSkillBinding binding : projected) {
                summaries.put(binding.getSkillId(), binding.getResourceSummary());
            }
            normalized.forEach(binding -> binding.setResourceSummary(summaries.get(binding.getSkillId())));
            remove(QueryWrapper.create()
                    .eq(AgentSkillBinding::getTenantId, agent.getTenantId())
                    .eq(AgentSkillBinding::getAgentId, agentId));
            if (!normalized.isEmpty()) {
                saveBatch(normalized);
            }
            return normalized;
        });
    }

    /** {@inheritDoc} */
    @Override
    public List<AgentSkillBinding> listBindings(BigInteger agentId) {
        if (agentId == null) {
            return Collections.emptyList();
        }
        return list(QueryWrapper.create()
                .eq(AgentSkillBinding::getAgentId, agentId)
                .orderBy(AgentSkillBinding::getSortNo, true)
                .orderBy(AgentSkillBinding::getId, true));
    }

    /** {@inheritDoc} */
    @Override
    public List<AgentSkillBinding> listSummaries(BigInteger agentId) {
        List<AgentSkillBinding> bindings = listBindings(agentId);
        Agent agent = agentMapper.selectOneById(agentId);
        Map<BigInteger, String> publishedHashes = publishedRuntimeHashes(agent);
        for (AgentSkillBinding binding : bindings) {
            Skill skill = skillService.getById(binding.getSkillId());
            if (skill == null) {
                binding.setResourceSummary(Map.of(
                        "skillId", binding.getSkillId(),
                        "displayName", "已失效技能",
                        "available", false));
                continue;
            }
            binding.setResourceSummary(runtimeProjector.currentSummary(
                    skill, publishedHashes.get(binding.getSkillId())));
            binding.getResourceSummary().put("available", true);
        }
        return bindings;
    }

    /**
     * 规范客户端绑定并写入服务端归属、排序和审计字段。
     *
     * @param agent Agent
     * @param bindings 客户端绑定
     * @return 规范绑定
     */
    private List<AgentSkillBinding> normalize(Agent agent, List<AgentSkillBinding> bindings) {
        if (bindings == null || bindings.isEmpty()) {
            return List.of();
        }
        if (bindings.size() > AgentSkillRuntimeProjector.MAX_SKILL_COUNT) {
            throw new BusinessException(409, 4092, "单个 Agent 最多可绑定 20 个 Skill");
        }
        Set<BigInteger> unique = new HashSet<>();
        List<AgentSkillBinding> result = new ArrayList<>();
        LoginAccount account = requireAccount();
        Date now = new Date();
        for (int index = 0; index < bindings.size(); index++) {
            AgentSkillBinding source = bindings.get(index);
            if (source == null || source.getSkillId() == null) {
                throw new BusinessException("Agent Skill 绑定参数不完整");
            }
            if (!unique.add(source.getSkillId())) {
                throw new BusinessException(409, 4092, "同一 Skill 不能重复绑定");
            }
            AgentSkillBinding binding = new AgentSkillBinding();
            binding.setTenantId(agent.getTenantId());
            binding.setAgentId(agent.getId());
            binding.setSkillId(source.getSkillId());
            binding.setSortNo(index);
            binding.setCreated(now);
            binding.setCreatedBy(account.getId());
            binding.setModified(now);
            binding.setModifiedBy(account.getId());
            result.add(binding);
        }
        return result;
    }

    /**
     * 查询并锁定 Agent。
     *
     * @param agentId Agent ID
     * @return Agent
     */
    private Agent requireAgentForUpdate(BigInteger agentId) {
        if (agentId == null) {
            throw new BusinessException("Agent ID 不能为空");
        }
        Agent agent = agentMapper.selectOneByQuery(QueryWrapper.create()
                .eq(Agent::getId, agentId)
                .forUpdate());
        if (agent == null) {
            throw new BusinessException(404, 404, "Agent 不存在");
        }
        return agent;
    }

    /**
     * 获取 Agent 当前线上冻结的 Skill 组合 hash。
     *
     * @param agentId Agent ID
     * @param skillId Skill ID
     * @return 组合 hash 或 null
     */
    private Map<BigInteger, String> publishedRuntimeHashes(Agent agent) {
        Map<BigInteger, String> hashes = new HashMap<>();
        Map<String, Object> snapshot = agent == null ? null : agent.getPublishedSnapshotJson();
        Object rawBindings = snapshot == null ? null : snapshot.get("skillBindings");
        if (!(rawBindings instanceof List<?> items)) {
            return hashes;
        }
        for (Object raw : items) {
            if (!(raw instanceof Map<?, ?> item) || item.get("skillId") == null) {
                continue;
            }
            Object resource = item.get("resourceSnapshot");
            if (resource instanceof Map<?, ?> resourceMap) {
                Object hash = resourceMap.get("skillRuntimeSnapshotHash");
                if (hash != null) {
                    hashes.put(new BigInteger(String.valueOf(item.get("skillId"))), String.valueOf(hash));
                }
            }
        }
        return hashes;
    }

    /**
     * 获取当前登录账号。
     *
     * @return 登录账号
     */
    private LoginAccount requireAccount() {
        LoginAccount account = SaTokenUtil.getLoginAccount();
        if (account == null || account.getId() == null) {
            throw new BusinessException(401, 401, "当前登录状态失效，请重新登录后再试");
        }
        return account;
    }
}
