package tech.easyflow.skill.service.impl;

import com.easyagents.agent.runtime.mcp.McpToolManifestEntry;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Test;
import org.springframework.beans.factory.ObjectProvider;
import tech.easyflow.skill.entity.Skill;
import tech.easyflow.skill.service.SkillCategoryService;
import tech.easyflow.skill.service.SkillReferenceProvider;
import tech.easyflow.skill.service.SkillResourceService;
import tech.easyflow.skill.service.SkillToolBindingService;
import tech.easyflow.skill.store.DBSkillContentStore;
import tech.easyflow.system.service.CategoryPermissionService;
import tech.easyflow.system.service.ResourceAccessService;

import java.lang.reflect.Method;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import static org.mockito.Mockito.mock;

/**
 * Skill 组合发布快照 hash 测试。
 */
public class SkillServiceImplSnapshotHashTest {

    /**
     * 含 MCP Manifest POJO 的组合快照经过 JSON 持久化后仍应通过校验。
     *
     * @throws Exception JSON 或反射调用失败时抛出
     */
    @Test
    @SuppressWarnings("unchecked")
    public void shouldVerifyAggregateSnapshotAfterJsonRoundTrip() throws Exception {
        ObjectMapper objectMapper = new ObjectMapper();
        SkillToolBindingService toolBindingService = mock(SkillToolBindingService.class);
        SkillServiceImpl service = service(objectMapper, toolBindingService);

        Map<String, Object> contentSnapshot = new LinkedHashMap<>();
        contentSnapshot.put("schemaVersion", 2);
        contentSnapshot.put("name", "l21-mcp-docs");
        contentSnapshot.put("snapshotHash", hash(service, contentSnapshot));

        Map<String, Object> binding = new LinkedHashMap<>();
        binding.put("toolType", "MCP");
        McpToolManifestEntry manifestEntry = new McpToolManifestEntry();
        manifestEntry.setName("query-docs");
        manifestEntry.setDescription("查询文档");
        manifestEntry.setInputSchema(Map.of("type", "object"));
        manifestEntry.setOutputSchema(Map.of());
        binding.put("mcpToolManifest", List.of(manifestEntry));
        Map<String, Object> toolSnapshot = new LinkedHashMap<>();
        toolSnapshot.put("schemaVersion", 1);
        toolSnapshot.put("bindings", List.of(binding));
        toolSnapshot.put("snapshotHash", "verified-by-tool-service");

        Map<String, Object> aggregate = new LinkedHashMap<>(contentSnapshot);
        Object contentHash = aggregate.remove("snapshotHash");
        aggregate.put("contentSnapshotHash", contentHash);
        aggregate.put("platformToolBindings", toolSnapshot);
        aggregate.put("toolBindingsHash", toolSnapshot.get("snapshotHash"));

        Skill persistedSkill = new Skill();
        persistedSkill.setPublishedSnapshotJson(objectMapper.readValue(
                objectMapper.writeValueAsBytes(contentSnapshot), Map.class));
        persistedSkill.setPublishedToolBindingsJson(objectMapper.readValue(
                objectMapper.writeValueAsBytes(toolSnapshot), Map.class));
        persistedSkill.setSnapshotHash(hash(service, aggregate));

        service.assertPublishedAggregateHash(persistedSkill);
    }

    /**
     * 创建仅用于快照校验的服务。
     *
     * @param objectMapper JSON 映射器
     * @param toolBindingService Tool 快照服务
     * @return Skill 服务
     */
    @SuppressWarnings("unchecked")
    private SkillServiceImpl service(ObjectMapper objectMapper,
                                     SkillToolBindingService toolBindingService) {
        return new SkillServiceImpl(
                mock(SkillCategoryService.class),
                mock(SkillResourceService.class),
                toolBindingService,
                mock(DBSkillContentStore.class),
                mock(ResourceAccessService.class),
                mock(CategoryPermissionService.class),
                objectMapper,
                mock(ObjectProvider.class));
    }

    /**
     * 调用生产代码的统一快照 hash 算法。
     *
     * @param service Skill 服务
     * @param value 待计算结构
     * @return SHA-256 hash
     * @throws Exception 反射调用失败时抛出
     */
    private String hash(SkillServiceImpl service, Object value) throws Exception {
        Method method = SkillServiceImpl.class.getDeclaredMethod("hashJson", Object.class);
        method.setAccessible(true);
        return (String) method.invoke(service, value);
    }
}
