package com.easyagents.federation.sql.federation;

import com.easyagents.federation.sql.source.SourceId;
import java.time.Instant;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import org.junit.Assert;
import org.junit.Test;

/**
 * 统计快照冻结语义测试。
 */
public class FederationStatisticsSnapshotTest {

    /**
     * 验证快照复制输入映射并采用最早统计有效期。
     */
    @Test
    public void shouldFreezeStatisticsAndExposeEarliestExpiry() {
        SourceId sourceId = new SourceId("source");
        Instant firstExpiry = Instant.parse("2026-08-23T10:00:00Z");
        Map<FederationStatisticsSnapshot.TableKey, FederationTableStatistics> mutable =
            new LinkedHashMap<>();
        mutable.put(
            new FederationStatisticsSnapshot.TableKey(sourceId, "PUBLIC", "ORDERS"),
            statistics(firstExpiry)
        );
        FederationStatisticsSnapshot snapshot = new FederationStatisticsSnapshot(
            "snapshot-1",
            Instant.parse("2026-08-23T09:00:00Z"),
            mutable
        );
        mutable.clear();

        Assert.assertNotNull(snapshot.statistics(sourceId, "public", "orders"));
        Assert.assertEquals(firstExpiry, snapshot.validUntil());
    }

    /**
     * 验证查询级统计指纹不会被未引用表的刷新影响。
     */
    @Test
    public void shouldFingerprintOnlySelectedTables() {
        SourceId sourceId = new SourceId("source");
        Instant capturedAt = Instant.parse("2026-08-23T09:00:00Z");
        Instant selectedExpiry = Instant.parse("2026-08-23T10:00:00Z");
        FederationStatisticsSnapshot.TableKey selected =
            new FederationStatisticsSnapshot.TableKey(sourceId, "public", "orders");
        FederationStatisticsSnapshot.TableKey unrelated =
            new FederationStatisticsSnapshot.TableKey(sourceId, "public", "customers");
        FederationStatisticsSnapshot first = new FederationStatisticsSnapshot(
            "global-v1",
            capturedAt,
            Map.of(
                selected, statistics(selectedExpiry),
                unrelated, statistics(capturedAt.plusSeconds(30))
            )
        );
        FederationStatisticsSnapshot second = new FederationStatisticsSnapshot(
            "global-v2",
            capturedAt,
            Map.of(
                selected, statistics(selectedExpiry),
                unrelated, statistics(capturedAt.plusSeconds(300))
            )
        );

        FederationStatisticsSnapshot.Selection firstSelection = first.select(Set.of(selected));
        FederationStatisticsSnapshot.Selection secondSelection = second.select(Set.of(selected));

        Assert.assertEquals(firstSelection.fingerprint(), secondSelection.fingerprint());
        Assert.assertEquals(selectedExpiry, firstSelection.validUntil());
        Assert.assertEquals(selectedExpiry, secondSelection.validUntil());
    }

    /**
     * 验证被查询表的统计变化会生成新的查询级指纹。
     */
    @Test
    public void shouldChangeFingerprintWhenSelectedTableStatisticsChange() {
        SourceId sourceId = new SourceId("source");
        Instant capturedAt = Instant.parse("2026-08-23T09:00:00Z");
        FederationStatisticsSnapshot.TableKey selected =
            new FederationStatisticsSnapshot.TableKey(sourceId, "public", "orders");
        FederationStatisticsSnapshot first = new FederationStatisticsSnapshot(
            "global-v1",
            capturedAt,
            Map.of(selected, statistics(capturedAt.plusSeconds(60)))
        );
        FederationStatisticsSnapshot second = new FederationStatisticsSnapshot(
            "global-v2",
            capturedAt,
            Map.of(selected, new FederationTableStatistics(
                200,
                64,
                Instant.parse("2026-08-23T08:00:00Z"),
                "catalog",
                Map.of(),
                java.util.List.of(),
                capturedAt.plusSeconds(60),
                FederationStatisticsStatus.COMPLETE
            ))
        );

        Assert.assertNotEquals(
            first.select(Set.of(selected)).fingerprint(),
            second.select(Set.of(selected)).fingerprint()
        );
    }

    /**
     * 验证统计在失效边界时立即按过期处理。
     */
    @Test
    public void shouldTreatExactExpiryBoundaryAsStale() {
        Instant boundary = Instant.parse("2026-08-23T10:00:00Z");

        Assert.assertEquals(
            FederationStatisticsStatus.STALE,
            statistics(boundary).effectiveStatus(boundary)
        );
    }

    /**
     * 验证已过期统计仍参与稳定指纹，但不会让降级计划在生成时立即失效。
     */
    @Test
    public void shouldKeepStaleFallbackSelectionCacheable() {
        Instant capturedAt = Instant.parse("2026-08-23T10:00:00Z");
        SourceId sourceId = new SourceId("source");
        FederationStatisticsSnapshot.TableKey table =
            new FederationStatisticsSnapshot.TableKey(sourceId, "public", "orders");
        FederationStatisticsSnapshot snapshot = new FederationStatisticsSnapshot(
            "stale-snapshot",
            capturedAt,
            Map.of(table, statistics(capturedAt.minusSeconds(1)))
        );

        FederationStatisticsSnapshot.Selection selection = snapshot.select(Set.of(table));

        Assert.assertEquals(Instant.MAX, selection.validUntil());
        Assert.assertFalse(selection.fingerprint().isBlank());
    }

    private static FederationTableStatistics statistics(Instant expiresAt) {
        return new FederationTableStatistics(
            100,
            64,
            Instant.parse("2026-08-23T08:00:00Z"),
            "catalog",
            Map.of(),
            java.util.List.of(),
            expiresAt,
            FederationStatisticsStatus.COMPLETE
        );
    }
}
