package com.mosquito.project.config;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.ValueSource;

import static org.assertj.core.api.Assertions.assertThat;

class AppConfigTest {

    @Test
    void shouldHaveDefaultSecurityConfigValues_whenInstantiated() {
        AppConfig config = new AppConfig();
        AppConfig.SecurityConfig security = config.getSecurity();

        assertThat(security.getApiKeyIterations()).isEqualTo(185000);
        assertThat(security.getEncryptionKey()).isEqualTo("default-32-byte-key-for-dev-only!!");
        assertThat(security.getIntrospection()).isNotNull();
    }

    @Test
    void shouldAllowCustomSecurityConfigValues_whenSet() {
        AppConfig config = new AppConfig();
        AppConfig.SecurityConfig security = new AppConfig.SecurityConfig();
        security.setApiKeyIterations(200000);
        security.setEncryptionKey("custom-encryption-key-for-testing!");
        config.setSecurity(security);

        assertThat(config.getSecurity().getApiKeyIterations()).isEqualTo(200000);
        assertThat(config.getSecurity().getEncryptionKey()).isEqualTo("custom-encryption-key-for-testing!");
    }

    @Test
    void shouldHaveDefaultIntrospectionConfigValues_whenInstantiated() {
        AppConfig config = new AppConfig();
        AppConfig.IntrospectionConfig introspection = config.getSecurity().getIntrospection();

        assertThat(introspection.getUrl()).isEmpty();
        assertThat(introspection.getClientId()).isEmpty();
        assertThat(introspection.getClientSecret()).isEmpty();
        assertThat(introspection.getTimeoutMillis()).isEqualTo(2000);
        assertThat(introspection.getCacheTtlSeconds()).isEqualTo(60);
        assertThat(introspection.getNegativeCacheSeconds()).isEqualTo(5);
    }

    @Test
    void shouldAllowCustomIntrospectionConfigValues_whenSet() {
        AppConfig config = new AppConfig();
        AppConfig.IntrospectionConfig introspection = new AppConfig.IntrospectionConfig();
        introspection.setUrl("https://auth.example.com/introspect");
        introspection.setClientId("test-client");
        introspection.setClientSecret("test-secret");
        introspection.setTimeoutMillis(5000);
        introspection.setCacheTtlSeconds(120);
        introspection.setNegativeCacheSeconds(10);
        config.getSecurity().setIntrospection(introspection);

        assertThat(config.getSecurity().getIntrospection().getUrl())
            .isEqualTo("https://auth.example.com/introspect");
        assertThat(config.getSecurity().getIntrospection().getClientId()).isEqualTo("test-client");
        assertThat(config.getSecurity().getIntrospection().getClientSecret()).isEqualTo("test-secret");
        assertThat(config.getSecurity().getIntrospection().getTimeoutMillis()).isEqualTo(5000);
        assertThat(config.getSecurity().getIntrospection().getCacheTtlSeconds()).isEqualTo(120);
        assertThat(config.getSecurity().getIntrospection().getNegativeCacheSeconds()).isEqualTo(10);
    }

    @Test
    void shouldHaveDefaultShortLinkConfigValues_whenInstantiated() {
        AppConfig config = new AppConfig();
        AppConfig.ShortLinkConfig shortLink = config.getShortLink();

        assertThat(shortLink.getCodeLength()).isEqualTo(8);
        assertThat(shortLink.getMaxUrlLength()).isEqualTo(2048);
        assertThat(shortLink.getLandingBaseUrl()).isEqualTo("https://example.com/landing");
        assertThat(shortLink.getCdnBaseUrl()).isEqualTo("https://cdn.example.com");
    }

    @ParameterizedTest
    @CsvSource({
        "1, 1",
        "8, 8",
        "16, 16",
        "32, 32"
    })
    void shouldAcceptValidCodeLengthValues_whenSet(int input, int expected) {
        AppConfig config = new AppConfig();
        config.getShortLink().setCodeLength(input);

        assertThat(config.getShortLink().getCodeLength()).isEqualTo(expected);
    }

    @ParameterizedTest
    @CsvSource({
        "128, 128",
        "1024, 1024",
        "2048, 2048",
        "4096, 4096"
    })
    void shouldAcceptValidMaxUrlLengthValues_whenSet(int input, int expected) {
        AppConfig config = new AppConfig();
        config.getShortLink().setMaxUrlLength(input);

        assertThat(config.getShortLink().getMaxUrlLength()).isEqualTo(expected);
    }

    @Test
    void shouldAllowCustomShortLinkUrls_whenSet() {
        AppConfig config = new AppConfig();
        config.getShortLink().setLandingBaseUrl("https://myapp.com/landing");
        config.getShortLink().setCdnBaseUrl("https://cdn.myapp.com");

        assertThat(config.getShortLink().getLandingBaseUrl()).isEqualTo("https://myapp.com/landing");
        assertThat(config.getShortLink().getCdnBaseUrl()).isEqualTo("https://cdn.myapp.com");
    }

    @Test
    void shouldHaveDefaultRateLimitConfigValues_whenInstantiated() {
        AppConfig config = new AppConfig();
        AppConfig.RateLimitConfig rateLimit = config.getRateLimit();

        assertThat(rateLimit.getPerMinute()).isEqualTo(100);
    }

    @ParameterizedTest
    @ValueSource(ints = {1, 10, 100, 1000, 10000})
    void shouldAcceptValidRateLimitValues_whenSet(int value) {
        AppConfig config = new AppConfig();
        config.getRateLimit().setPerMinute(value);

        assertThat(config.getRateLimit().getPerMinute()).isEqualTo(value);
    }

    @Test
    void shouldHaveDefaultCacheConfigValues_whenInstantiated() {
        AppConfig config = new AppConfig();
        AppConfig.CacheConfig cache = config.getCache();

        assertThat(cache.getLeaderboardTtlMinutes()).isEqualTo(5);
        assertThat(cache.getActivityTtlMinutes()).isEqualTo(1);
        assertThat(cache.getStatsTtlMinutes()).isEqualTo(2);
        assertThat(cache.getGraphTtlMinutes()).isEqualTo(10);
    }

    @ParameterizedTest
    @CsvSource({
        "leaderboardTtlMinutes, 5, 10",
        "activityTtlMinutes, 1, 5",
        "statsTtlMinutes, 2, 15",
        "graphTtlMinutes, 10, 30"
    })
    void shouldAllowCustomCacheTtlValues_whenSet(String property, int defaultValue, int newValue) {
        AppConfig config = new AppConfig();
        AppConfig.CacheConfig cache = config.getCache();

        switch (property) {
            case "leaderboardTtlMinutes":
                assertThat(cache.getLeaderboardTtlMinutes()).isEqualTo(defaultValue);
                cache.setLeaderboardTtlMinutes(newValue);
                assertThat(cache.getLeaderboardTtlMinutes()).isEqualTo(newValue);
                break;
            case "activityTtlMinutes":
                assertThat(cache.getActivityTtlMinutes()).isEqualTo(defaultValue);
                cache.setActivityTtlMinutes(newValue);
                assertThat(cache.getActivityTtlMinutes()).isEqualTo(newValue);
                break;
            case "statsTtlMinutes":
                assertThat(cache.getStatsTtlMinutes()).isEqualTo(defaultValue);
                cache.setStatsTtlMinutes(newValue);
                assertThat(cache.getStatsTtlMinutes()).isEqualTo(newValue);
                break;
            case "graphTtlMinutes":
                assertThat(cache.getGraphTtlMinutes()).isEqualTo(defaultValue);
                cache.setGraphTtlMinutes(newValue);
                assertThat(cache.getGraphTtlMinutes()).isEqualTo(newValue);
                break;
        }
    }

    @Test
    void shouldVerifySetterGetterConsistency_forAllCacheConfigProperties() {
        AppConfig.CacheConfig cache = new AppConfig.CacheConfig();

        cache.setLeaderboardTtlMinutes(15);
        assertThat(cache.getLeaderboardTtlMinutes()).isEqualTo(15);

        cache.setActivityTtlMinutes(3);
        assertThat(cache.getActivityTtlMinutes()).isEqualTo(3);

        cache.setStatsTtlMinutes(5);
        assertThat(cache.getStatsTtlMinutes()).isEqualTo(5);

        cache.setGraphTtlMinutes(20);
        assertThat(cache.getGraphTtlMinutes()).isEqualTo(20);
    }

    @Test
    void shouldVerifySetterGetterConsistency_forAllShortLinkConfigProperties() {
        AppConfig.ShortLinkConfig shortLink = new AppConfig.ShortLinkConfig();

        shortLink.setCodeLength(12);
        assertThat(shortLink.getCodeLength()).isEqualTo(12);

        shortLink.setMaxUrlLength(4096);
        assertThat(shortLink.getMaxUrlLength()).isEqualTo(4096);

        shortLink.setLandingBaseUrl("https://test.com");
        assertThat(shortLink.getLandingBaseUrl()).isEqualTo("https://test.com");

        shortLink.setCdnBaseUrl("https://cdn.test.com");
        assertThat(shortLink.getCdnBaseUrl()).isEqualTo("https://cdn.test.com");
    }

    @Test
    void shouldVerifySetterGetterConsistency_forAllRateLimitConfigProperties() {
        AppConfig.RateLimitConfig rateLimit = new AppConfig.RateLimitConfig();

        rateLimit.setPerMinute(200);
        assertThat(rateLimit.getPerMinute()).isEqualTo(200);

        rateLimit.setPerMinute(50);
        assertThat(rateLimit.getPerMinute()).isEqualTo(50);
    }

    @Test
    void shouldVerifySetterGetterConsistency_forAllIntrospectionConfigProperties() {
        AppConfig.IntrospectionConfig introspection = new AppConfig.IntrospectionConfig();

        introspection.setUrl("https://auth.test.com");
        assertThat(introspection.getUrl()).isEqualTo("https://auth.test.com");

        introspection.setClientId("client123");
        assertThat(introspection.getClientId()).isEqualTo("client123");

        introspection.setClientSecret("secret456");
        assertThat(introspection.getClientSecret()).isEqualTo("secret456");

        introspection.setTimeoutMillis(3000);
        assertThat(introspection.getTimeoutMillis()).isEqualTo(3000);

        introspection.setCacheTtlSeconds(90);
        assertThat(introspection.getCacheTtlSeconds()).isEqualTo(90);

        introspection.setNegativeCacheSeconds(15);
        assertThat(introspection.getNegativeCacheSeconds()).isEqualTo(15);
    }

    @Test
    void shouldVerifySetterGetterConsistency_forAllSecurityConfigProperties() {
        AppConfig.SecurityConfig security = new AppConfig.SecurityConfig();

        security.setApiKeyIterations(250000);
        assertThat(security.getApiKeyIterations()).isEqualTo(250000);

        security.setEncryptionKey("new-encryption-key-for-testing!!");
        assertThat(security.getEncryptionKey()).isEqualTo("new-encryption-key-for-testing!!");

        AppConfig.IntrospectionConfig newIntrospection = new AppConfig.IntrospectionConfig();
        newIntrospection.setUrl("https://new.auth.com");
        security.setIntrospection(newIntrospection);
        assertThat(security.getIntrospection().getUrl()).isEqualTo("https://new.auth.com");
    }

    @Test
    void shouldHandleEdgeCaseValues_forShortLinkCodeLength() {
        AppConfig.ShortLinkConfig shortLink = new AppConfig.ShortLinkConfig();

        shortLink.setCodeLength(0);
        assertThat(shortLink.getCodeLength()).isZero();

        shortLink.setCodeLength(Integer.MAX_VALUE);
        assertThat(shortLink.getCodeLength()).isEqualTo(Integer.MAX_VALUE);
    }

    @Test
    void shouldHandleEdgeCaseValues_forShortLinkMaxUrlLength() {
        AppConfig.ShortLinkConfig shortLink = new AppConfig.ShortLinkConfig();

        shortLink.setMaxUrlLength(0);
        assertThat(shortLink.getMaxUrlLength()).isZero();

        shortLink.setMaxUrlLength(Integer.MAX_VALUE);
        assertThat(shortLink.getMaxUrlLength()).isEqualTo(Integer.MAX_VALUE);
    }

    @Test
    void shouldHandleNullAndEmptyStrings_forStringProperties() {
        AppConfig.IntrospectionConfig introspection = new AppConfig.IntrospectionConfig();

        introspection.setUrl(null);
        assertThat(introspection.getUrl()).isNull();

        introspection.setClientId("");
        assertThat(introspection.getClientId()).isEmpty();

        introspection.setClientSecret("   ");
        assertThat(introspection.getClientSecret()).isEqualTo("   ");
    }

    @Test
    void shouldVerifyDefaultPosterConfig_whenInstantiated() {
        AppConfig config = new AppConfig();
        PosterConfig poster = config.getPoster();

        assertThat(poster).isNotNull();
        assertThat(poster.getDefaultTemplate()).isEqualTo("default");
        assertThat(poster.getTemplates()).isNotNull();
        assertThat(poster.getCdnBaseUrl()).isEqualTo("https://cdn.example.com");
    }

    @Test
    void shouldAllowCustomPosterConfig_whenSet() {
        AppConfig config = new AppConfig();
        PosterConfig poster = new PosterConfig();
        poster.setDefaultTemplate("custom");
        poster.setCdnBaseUrl("https://custom-cdn.com");
        config.setPoster(poster);

        assertThat(config.getPoster().getDefaultTemplate()).isEqualTo("custom");
        assertThat(config.getPoster().getCdnBaseUrl()).isEqualTo("https://custom-cdn.com");
    }

    @Test
    void shouldVerifyAllConfigObjectsAreInstantiated_whenNewAppConfigCreated() {
        AppConfig config = new AppConfig();

        assertThat(config.getSecurity()).isNotNull();
        assertThat(config.getShortLink()).isNotNull();
        assertThat(config.getRateLimit()).isNotNull();
        assertThat(config.getCache()).isNotNull();
        assertThat(config.getPoster()).isNotNull();

        assertThat(config.getSecurity().getIntrospection()).isNotNull();
    }
}
