package com.magistr.app.config.tenant.health;

import com.magistr.app.config.tenant.TenantConfig;
import org.junit.jupiter.api.Test;

import java.nio.charset.StandardCharsets;
import java.time.Clock;
import java.time.Duration;
import java.time.Instant;
import java.time.ZoneOffset;
import java.util.List;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

class TenantReadinessRegistryTest {

    private static final Instant NOW = Instant.parse("2026-07-15T10:00:00Z");
    private static final Clock CLOCK = Clock.fixed(NOW, ZoneOffset.UTC);
    private static final byte[] KEY = "0123456789abcdef0123456789abcdef".getBytes(StandardCharsets.UTF_8);

    @Test
    void пустойСписокИH2НеДолжныБытьГотовы() {
        TenantReadinessRegistry registry = registry();

        assertFalse(registry.aggregate(Duration.ofSeconds(30)).ready());

        TenantConfig h2 = config("default", "jdbc:h2:mem:placeholder", "sa", "секрет");
        registry.replaceDesired(List.of(h2));
        registry.markMigrationSucceeded(h2);

        assertFalse(registry.markConnectivity("default", true));
        TenantReadinessRegistry.AggregateReadiness readiness = registry.aggregate(Duration.ofSeconds(30));
        assertFalse(readiness.ready());
        assertEquals(1, readiness.unsupportedTenants());
    }

    @Test
    void готовностьТребуетУспешнуюМиграциюИСвежееСоединение() {
        TenantReadinessRegistry registry = registry();
        TenantConfig tenant = config("alpha", "jdbc:postgresql://db/alpha", "user", "secret");

        registry.replaceDesired(List.of(tenant));
        registry.markMigrationStarted(tenant);
        assertFalse(registry.aggregate(Duration.ofSeconds(30)).ready());

        registry.markMigrationSucceeded(tenant);
        assertFalse(registry.aggregate(Duration.ofSeconds(30)).ready());

        assertTrue(registry.markConnectivity("alpha", true));
        TenantReadinessRegistry.AggregateReadiness readiness = registry.aggregate(Duration.ofSeconds(30));
        assertTrue(readiness.ready());
        assertEquals("ГОТОВ", readiness.status());
        assertEquals(1, readiness.readyTenants());
    }

    @Test
    void устаревшаяПроверкаИFingerprintСтаройКонфигурацииИгнорируются() {
        TenantReadinessRegistry registry = registry();
        TenantConfig first = config("alpha", "jdbc:postgresql://db/alpha", "user", "first-secret");
        registry.replaceDesired(List.of(first));
        registry.markMigrationSucceeded(first);
        TenantReadinessRegistry.ConnectivityTarget oldTarget = registry.connectivityTargets().get(0);

        TenantConfig changed = config("alpha", "jdbc:postgresql://db/alpha", "user", "second-secret");
        registry.replaceDesired(List.of(changed));
        registry.markMigrationSucceeded(changed);
        TenantReadinessRegistry.ConnectivityTarget currentTarget = registry.connectivityTargets().get(0);

        assertNotEquals(oldTarget.fingerprint(), currentTarget.fingerprint());
        assertFalse(registry.markConnectivity("alpha", oldTarget.fingerprint(), true, NOW));
        assertTrue(registry.markConnectivity("alpha", currentTarget.fingerprint(), true, NOW));
        assertFalse(registry.markConnectivity(
                "alpha", currentTarget.fingerprint(), false, NOW.minusSeconds(1)));
        assertTrue(registry.aggregate(Duration.ofSeconds(30)).ready());
    }

    @Test
    void эквивалентнаяНормализованнаяКонфигурацияНеСбрасываетГотовность() {
        TenantReadinessRegistry registry = registry();
        TenantConfig raw = new TenantConfig(
                "  Университет  ",
                " ALPHA ",
                "  jdbc:postgresql://db/alpha  ",
                "user",
                "secret"
        );
        TenantConfig normalized = new TenantConfig(
                "Университет",
                "alpha",
                "jdbc:postgresql://db/alpha",
                "user",
                "secret"
        );

        registry.replaceDesired(List.of(raw));
        registry.markMigrationSucceeded(raw);
        registry.markConnectivity("alpha", true);
        String before = registry.snapshot().get("alpha").fingerprint();

        registry.replaceDesired(List.of(normalized));

        assertEquals(before, registry.snapshot().get("alpha").fingerprint());
        assertTrue(registry.aggregate(Duration.ofSeconds(30)).ready());
    }

    @Test
    void fingerprintНеРаскрываетРеквизиты() {
        TenantReadinessRegistry registry = registry();
        TenantConfig tenant = config(
                "secret-domain",
                "jdbc:postgresql://private-host/private-db",
                "private-user",
                "private-password");

        registry.replaceDesired(List.of(tenant));
        String fingerprint = registry.snapshot().get("secret-domain").fingerprint();

        assertFalse(fingerprint.contains("private-host"));
        assertFalse(fingerprint.contains("private-user"));
        assertFalse(fingerprint.contains("private-password"));
        assertFalse(fingerprint.contains("secret-domain"));
    }

    @Test
    void явнаяОшибкаКонфигурацииСохраняетсяДоЯвнойОчистки() {
        TenantReadinessRegistry registry = registry();
        TenantConfig tenant = config("alpha", "jdbc:postgresql://db/alpha", "user", "secret");

        registry.markConfigurationFailure();
        registry.replaceDesired(List.of(tenant));
        registry.markMigrationSucceeded(tenant);
        registry.markConnectivity("alpha", true);

        assertTrue(registry.aggregate(Duration.ofSeconds(30)).configurationFailure());
        assertFalse(registry.aggregate(Duration.ofSeconds(30)).ready());

        registry.clearConfigurationFailure();
        assertTrue(registry.aggregate(Duration.ofSeconds(30)).ready());
    }

    @Test
    void просроченнаяПроверкаДелаетСервисНеготовым() {
        TenantReadinessRegistry registry = registry();
        TenantConfig tenant = config("alpha", "jdbc:postgresql://db/alpha", "user", "secret");
        registry.replaceDesired(List.of(tenant));
        registry.markMigrationSucceeded(tenant);
        TenantReadinessRegistry.ConnectivityTarget target = registry.connectivityTargets().get(0);

        registry.markConnectivity("alpha", target.fingerprint(), true, NOW.minusSeconds(31));

        TenantReadinessRegistry.AggregateReadiness readiness = registry.aggregate(Duration.ofSeconds(30));
        assertFalse(readiness.ready());
        assertEquals(1, readiness.staleConnections());
    }

    private TenantReadinessRegistry registry() {
        return new TenantReadinessRegistry(CLOCK, KEY);
    }

    private TenantConfig config(String domain, String url, String username, String password) {
        return new TenantConfig(domain, domain, url, username, password);
    }
}
