package de.gecheckt.pdf.umbenenner.adapter.out.validation;

import static org.junit.jupiter.api.Assertions.assertEquals;

import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;

import org.junit.jupiter.api.Test;

import de.gecheckt.pdf.umbenenner.application.config.provider.AiProviderFamily;
import de.gecheckt.pdf.umbenenner.application.port.out.modelcatalog.ApiKeyOrigin;
import de.gecheckt.pdf.umbenenner.application.port.out.modelcatalog.EffectiveApiKeyDescriptor;

/**
 * Unit tests for {@link EnvironmentApiKeyResolutionAdapter}.
 * <p>
 * The environment lookup is injected via the package-private constructor so that tests
 * are deterministic and do not depend on real process environment variables.
 */
class EnvironmentApiKeyResolutionAdapterTest {

    // =========================================================================
    // Helper
    // =========================================================================

    private static EnvironmentApiKeyResolutionAdapter adapterWith(Map<String, String> env) {
        Function<String, String> lookup = env::get;
        return new EnvironmentApiKeyResolutionAdapter(lookup);
    }

    // =========================================================================
    // Claude – provider env var takes precedence
    // =========================================================================

    @Test
    void claude_envVarPresent_returnsFromProviderEnvVar() {
        Map<String, String> env = Map.of(
                EnvironmentApiKeyResolutionAdapter.ENV_CLAUDE_API_KEY, "sk-ant-test");

        EffectiveApiKeyDescriptor result = adapterWith(env)
                .resolve(AiProviderFamily.CLAUDE, "");

        assertEquals(ApiKeyOrigin.FROM_PROVIDER_ENV_VAR, result.origin());
        assertEquals(EnvironmentApiKeyResolutionAdapter.ENV_CLAUDE_API_KEY,
                result.envVarName().orElseThrow());
    }

    @Test
    void claude_envVarPresentAndPropertyAlsoPresent_envVarWins() {
        Map<String, String> env = Map.of(
                EnvironmentApiKeyResolutionAdapter.ENV_CLAUDE_API_KEY, "sk-ant-env");

        EffectiveApiKeyDescriptor result = adapterWith(env)
                .resolve(AiProviderFamily.CLAUDE, "sk-ant-property");

        assertEquals(ApiKeyOrigin.FROM_PROVIDER_ENV_VAR, result.origin());
    }

    // =========================================================================
    // Claude – property file fallback
    // =========================================================================

    @Test
    void claude_noEnvVar_propertyPresent_returnsFromPropertyFile() {
        EffectiveApiKeyDescriptor result = adapterWith(Map.of())
                .resolve(AiProviderFamily.CLAUDE, "sk-ant-property");

        assertEquals(ApiKeyOrigin.FROM_PROPERTY_FILE, result.origin());
    }

    // =========================================================================
    // Claude – absent
    // =========================================================================

    @Test
    void claude_noEnvVar_noProperty_returnsAbsent() {
        EffectiveApiKeyDescriptor result = adapterWith(Map.of())
                .resolve(AiProviderFamily.CLAUDE, "");

        assertEquals(ApiKeyOrigin.ABSENT, result.origin());
    }

    @Test
    void claude_noEnvVar_blankPropertyOnly_returnsAbsent() {
        EffectiveApiKeyDescriptor result = adapterWith(Map.of())
                .resolve(AiProviderFamily.CLAUDE, "   ");

        assertEquals(ApiKeyOrigin.ABSENT, result.origin());
    }

    // =========================================================================
    // OpenAI-compatible – primary env var takes precedence
    // =========================================================================

    @Test
    void openai_primaryEnvVarPresent_returnsFromProviderEnvVar() {
        Map<String, String> env = Map.of(
                EnvironmentApiKeyResolutionAdapter.ENV_OPENAI_API_KEY, "sk-openai-test");

        EffectiveApiKeyDescriptor result = adapterWith(env)
                .resolve(AiProviderFamily.OPENAI_COMPATIBLE, "");

        assertEquals(ApiKeyOrigin.FROM_PROVIDER_ENV_VAR, result.origin());
        assertEquals(EnvironmentApiKeyResolutionAdapter.ENV_OPENAI_API_KEY,
                result.envVarName().orElseThrow());
    }

    @Test
    void openai_primaryEnvVarAndLegacyBothPresent_primaryWins() {
        Map<String, String> env = Map.of(
                EnvironmentApiKeyResolutionAdapter.ENV_OPENAI_API_KEY, "sk-primary",
                EnvironmentApiKeyResolutionAdapter.ENV_LEGACY_OPENAI_API_KEY, "sk-legacy");

        EffectiveApiKeyDescriptor result = adapterWith(env)
                .resolve(AiProviderFamily.OPENAI_COMPATIBLE, "");

        assertEquals(ApiKeyOrigin.FROM_PROVIDER_ENV_VAR, result.origin());
        assertEquals(EnvironmentApiKeyResolutionAdapter.ENV_OPENAI_API_KEY,
                result.envVarName().orElseThrow());
    }

    // =========================================================================
    // OpenAI-compatible – legacy env var
    // =========================================================================

    @Test
    void openai_noPrimaryEnvVar_legacyPresent_returnsFromLegacyEnvVar() {
        Map<String, String> env = Map.of(
                EnvironmentApiKeyResolutionAdapter.ENV_LEGACY_OPENAI_API_KEY, "sk-legacy-key");

        EffectiveApiKeyDescriptor result = adapterWith(env)
                .resolve(AiProviderFamily.OPENAI_COMPATIBLE, "");

        assertEquals(ApiKeyOrigin.FROM_LEGACY_ENV_VAR, result.origin());
        assertEquals(EnvironmentApiKeyResolutionAdapter.ENV_LEGACY_OPENAI_API_KEY,
                result.envVarName().orElseThrow());
    }

    @Test
    void openai_noPrimaryEnvVar_legacyPresentAndPropertyAlsoPresent_legacyWins() {
        Map<String, String> env = Map.of(
                EnvironmentApiKeyResolutionAdapter.ENV_LEGACY_OPENAI_API_KEY, "sk-legacy");

        EffectiveApiKeyDescriptor result = adapterWith(env)
                .resolve(AiProviderFamily.OPENAI_COMPATIBLE, "sk-property");

        assertEquals(ApiKeyOrigin.FROM_LEGACY_ENV_VAR, result.origin());
    }

    // =========================================================================
    // OpenAI-compatible – property file fallback
    // =========================================================================

    @Test
    void openai_noEnvVars_propertyPresent_returnsFromPropertyFile() {
        EffectiveApiKeyDescriptor result = adapterWith(Map.of())
                .resolve(AiProviderFamily.OPENAI_COMPATIBLE, "sk-openai-property");

        assertEquals(ApiKeyOrigin.FROM_PROPERTY_FILE, result.origin());
    }

    // =========================================================================
    // OpenAI-compatible – absent
    // =========================================================================

    @Test
    void openai_noEnvVars_noProperty_returnsAbsent() {
        EffectiveApiKeyDescriptor result = adapterWith(Map.of())
                .resolve(AiProviderFamily.OPENAI_COMPATIBLE, "");

        assertEquals(ApiKeyOrigin.ABSENT, result.origin());
    }

    @Test
    void openai_noEnvVars_blankProperty_returnsAbsent() {
        EffectiveApiKeyDescriptor result = adapterWith(Map.of())
                .resolve(AiProviderFamily.OPENAI_COMPATIBLE, "  ");

        assertEquals(ApiKeyOrigin.ABSENT, result.origin());
    }

    // =========================================================================
    // Blank env value treated as absent
    // =========================================================================

    @Test
    void claude_envVarPresentButBlank_treatedAsAbsent_fallsBackToProperty() {
        Map<String, String> env = new HashMap<>();
        env.put(EnvironmentApiKeyResolutionAdapter.ENV_CLAUDE_API_KEY, "   ");

        EffectiveApiKeyDescriptor result = adapterWith(env)
                .resolve(AiProviderFamily.CLAUDE, "sk-prop");

        assertEquals(ApiKeyOrigin.FROM_PROPERTY_FILE, result.origin());
    }

    @Test
    void openai_primaryEnvVarBlank_legacyAbsent_propertyPresent_returnsFromPropertyFile() {
        Map<String, String> env = new HashMap<>();
        env.put(EnvironmentApiKeyResolutionAdapter.ENV_OPENAI_API_KEY, "");

        EffectiveApiKeyDescriptor result = adapterWith(env)
                .resolve(AiProviderFamily.OPENAI_COMPATIBLE, "sk-prop");

        assertEquals(ApiKeyOrigin.FROM_PROPERTY_FILE, result.origin());
    }
}
