package com.magistr.app.config.tenant;

import com.magistr.app.config.tenant.health.TenantReadinessRegistry;
import com.magistr.app.service.TenantDatabaseMigrationService;
import com.zaxxer.hikari.HikariDataSource;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.springframework.test.util.ReflectionTestUtils;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;

class TenantDataSourceConfigTest {

    private static final Duration MAX_CONNECTIVITY_AGE = Duration.ofSeconds(30);

    @TempDir
    Path tempDir;

    private final List<TenantRoutingDataSource> createdDataSources = new ArrayList<>();

    @AfterEach
    void closeDataSources() {
        createdDataSources.forEach(routingDataSource -> routingDataSource
                .getTenantDataSource("default")
                .filter(HikariDataSource.class::isInstance)
                .map(HikariDataSource.class::cast)
                .ifPresent(HikariDataSource::close));
    }

    @Test
    @DisplayName("Обязательный отсутствующий tenants.json переводит readiness в состояние DOWN")
    void requiredMissingConfigMarksConfigurationFailure() {
        TenantReadinessRegistry registry = new TenantReadinessRegistry();

        createDataSource(tempDir.resolve("missing-tenants.json"), true, registry);

        TenantReadinessRegistry.AggregateReadiness readiness = registry.aggregate(MAX_CONNECTIVITY_AGE);
        assertThat(readiness.configurationFailure()).isTrue();
        assertThat(readiness.ready()).isFalse();
    }

    @Test
    @DisplayName("Невалидный JSON переводит readiness в состояние DOWN")
    void invalidJsonMarksConfigurationFailure() throws IOException {
        Path configPath = tempDir.resolve("tenants.json");
        Files.writeString(configPath, "{невалидный-json");
        TenantReadinessRegistry registry = new TenantReadinessRegistry();

        createDataSource(configPath, false, registry);

        TenantReadinessRegistry.AggregateReadiness readiness = registry.aggregate(MAX_CONNECTIVITY_AGE);
        assertThat(readiness.configurationFailure()).isTrue();
        assertThat(readiness.ready()).isFalse();
    }

    @Test
    @DisplayName("Необязательный отсутствующий tenants.json не создаёт ошибку конфигурации")
    void optionalMissingConfigDoesNotMarkConfigurationFailure() {
        TenantReadinessRegistry registry = new TenantReadinessRegistry();

        createDataSource(tempDir.resolve("missing-tenants.json"), false, registry);

        TenantReadinessRegistry.AggregateReadiness readiness = registry.aggregate(MAX_CONNECTIVITY_AGE);
        assertThat(readiness.configurationFailure()).isFalse();
    }

    private void createDataSource(Path configPath,
                                  boolean configRequired,
                                  TenantReadinessRegistry registry) {
        TenantDataSourceConfig config = new TenantDataSourceConfig();
        ReflectionTestUtils.setField(config, "tenantsConfigPath", configPath.toString());
        ReflectionTestUtils.setField(config, "tenantsConfigRequired", configRequired);
        ReflectionTestUtils.setField(config, "defaultDbUrl", "");
        ReflectionTestUtils.setField(config, "defaultDbUsername", "");
        ReflectionTestUtils.setField(config, "defaultDbPassword", "");

        TenantRoutingDataSource routingDataSource = (TenantRoutingDataSource) config.dataSource(
                mock(TenantDatabaseMigrationService.class),
                registry
        );
        createdDataSources.add(routingDataSource);
    }
}
