package com.easyagents.federation.sql.source;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.Assert;
import org.junit.Test;

/**
 * FederationSourceDefinition 不可变性与校验和测试。
 */
public class FederationSourceDefinitionTest {

    /**
     * 验证构造后修改外部集合不会改变 Definition 与校验和。
     */
    @Test
    public void shouldDefensivelyCopyNestedCollections() {
        List<FederationSchemaDefinition> schemas = new ArrayList<>();
        schemas.add(new ExternalSchemaDefinition("app", "schema-ref", 1));
        Map<String, String> options = new HashMap<>();
        options.put("mode", "safe");

        FederationSourceDefinition definition = new FederationSourceDefinition(
            new SourceId("source-a"),
            7,
            "fake",
            schemas,
            options
        );
        String checksum = definition.checksum();

        schemas.clear();
        options.put("mode", "changed");

        Assert.assertEquals(1, definition.schemas().size());
        Assert.assertEquals("safe", definition.adapterOptions().get("mode"));
        Assert.assertEquals(checksum, definition.checksum());
    }

    /**
     * 验证同名 Schema 会在进入 Runtime 前被拒绝。
     */
    @Test(expected = IllegalArgumentException.class)
    public void shouldRejectDuplicateLogicalSchemaNames() {
        new FederationSourceDefinition(
            new SourceId("source-a"),
            1,
            "fake",
            List.of(
                new ExternalSchemaDefinition("app", "one", 1),
                new ExternalSchemaDefinition("app", "two", 1)
            ),
            Map.of()
        );
    }

    /**
     * 验证可空字段、分隔符字符和 Adapter 选项边界不会产生规范化碰撞。
     */
    @Test
    public void shouldUseUnambiguousCanonicalChecksumFields() {
        FederationSourceDefinition nullField = customDefinition(
            new ChecksumSchema("app", null, "x\0y"),
            Map.of("a", "bc")
        );
        FederationSourceDefinition literalField = customDefinition(
            new ChecksumSchema("app", "<null>", "x\0y"),
            Map.of("a", "bc")
        );
        FederationSourceDefinition shiftedBoundary = customDefinition(
            new ChecksumSchema("app", null, "x\0", "y"),
            Map.of("a", "bc")
        );
        FederationSourceDefinition optionBoundary = customDefinition(
            new ChecksumSchema("app", null, "x\0y"),
            Map.of("ab", "c")
        );

        Assert.assertNotEquals(nullField.checksum(), literalField.checksum());
        Assert.assertNotEquals(nullField.checksum(), shiftedBoundary.checksum());
        Assert.assertNotEquals(nullField.checksum(), optionBoundary.checksum());
    }

    /**
     * 验证共享状态不能携带伪造的 Definition 或墓碑校验和。
     */
    @Test
    public void shouldRejectForgedSharedStateChecksums() {
        FederationSourceDefinition definition = customDefinition(
            new ChecksumSchema("app", "catalog", "schema"),
            Map.of()
        );
        try {
            new ActiveSourceState(definition, "forged");
            Assert.fail("forged active checksum should be rejected");
        } catch (IllegalArgumentException expected) {
            Assert.assertTrue(expected.getMessage().contains("checksum"));
        }
        try {
            new SourceTombstone(definition.sourceId(), definition.revision() + 1, "forged");
            Assert.fail("forged tombstone checksum should be rejected");
        } catch (IllegalArgumentException expected) {
            Assert.assertTrue(expected.getMessage().contains("checksum"));
        }
    }

    private static FederationSourceDefinition customDefinition(
        FederationSchemaDefinition schema,
        Map<String, String> options
    ) {
        return new FederationSourceDefinition(
            new SourceId("source-a"),
            7,
            "fake",
            List.of(schema),
            options
        );
    }

    /**
     * 测试用可空、可含分隔符的 Schema 校验和字段。
     *
     * @param logicalName 逻辑名称
     * @param fields 校验和字段
     */
    private record ChecksumSchema(String logicalName, List<String> fields)
        implements FederationSchemaDefinition {

        private ChecksumSchema(String logicalName, String... fields) {
            this(logicalName, Arrays.asList(fields));
        }

        @Override
        public List<String> checksumFields() {
            return fields;
        }
    }
}
