package org.raddatz.familienarchiv.user;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.context.properties.source.ConfigurationPropertySources;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.io.ClassPathResource;

import java.lang.reflect.Field;
import java.util.Properties;

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

/**
 * Pins the admin-seed property key contract. {@code UserDataInitializer} reads
 * {@code @Value("${app.admin.email:...}")} and {@code @Value("${app.admin.password:...}")}.
 * The yaml MUST expose those exact keys, not e.g. {@code app.admin.username}, or
 * the env vars {@code APP_ADMIN_USERNAME} / {@code APP_ADMIN_PASSWORD} are
 * silently ignored and the admin user gets seeded with the hardcoded defaults.
 *
 * <p>Discovered as a HIGH bug during the production-deploy bootstrap (#513): on
 * first deploy the prod admin password is permanently locked to whatever ends
 * up in the database, so a key-name mismatch would lock prod to the dev defaults
 * {@code admin@familyarchive.local} / {@code admin123}.
 *
 * <p>No Spring context — Binder reads application.yaml directly.
 */
class AdminSeedPropertyKeyTest {

    @Test
    void admin_email_key_binds_from_yaml() {
        Binder binder = binderFromApplicationYaml();

        String email = binder.bind("app.admin.email", String.class)
            .orElseThrow(() -> new AssertionError(
                "app.admin.email is missing from application.yaml. "
                + "UserDataInitializer reads this exact key; if the yaml uses "
                + "a different name (e.g. 'username'), the env var "
                + "APP_ADMIN_USERNAME is silently ignored."));

        assertThat(email)
            .as("app.admin.email must resolve from APP_ADMIN_USERNAME or its default")
            .isNotBlank();
    }

    @Test
    void admin_password_key_binds_from_yaml() {
        Binder binder = binderFromApplicationYaml();

        String password = binder.bind("app.admin.password", String.class)
            .orElseThrow(() -> new AssertionError(
                "app.admin.password is missing from application.yaml. "
                + "UserDataInitializer reads this exact key."));

        assertThat(password)
            .as("app.admin.password must resolve from APP_ADMIN_PASSWORD or its default")
            .isNotBlank();
    }

    @Test
    void userDataInitializer_reads_app_admin_email_not_username() throws NoSuchFieldException {
        // Pin the Java side too: a future rename of the @Value placeholder
        // (e.g. back to `${app.admin.username:...}`) would silently break the
        // binding while the yaml-side assertions above still pass. See #513.
        Field field = UserDataInitializer.class.getDeclaredField("adminEmail");
        Value annotation = field.getAnnotation(Value.class);
        assertThat(annotation)
            .as("UserDataInitializer.adminEmail must be @Value-annotated")
            .isNotNull();
        assertThat(annotation.value())
            .as("UserDataInitializer must read app.admin.email — not username or any other key")
            .startsWith("${app.admin.email:");
    }

    @Test
    void userDataInitializer_reads_app_admin_password() throws NoSuchFieldException {
        Field field = UserDataInitializer.class.getDeclaredField("adminPassword");
        Value annotation = field.getAnnotation(Value.class);
        assertThat(annotation).isNotNull();
        assertThat(annotation.value())
            .as("UserDataInitializer must read app.admin.password")
            .startsWith("${app.admin.password:");
    }

    private Binder binderFromApplicationYaml() {
        YamlPropertiesFactoryBean yaml = new YamlPropertiesFactoryBean();
        yaml.setResources(new ClassPathResource("application.yaml"));
        Properties props = yaml.getObject();
        assertThat(props).as("application.yaml must be on the classpath").isNotNull();
        return new Binder(ConfigurationPropertySources.from(
            new PropertiesPropertySource("application", props)));
    }
}
