package com.cameleer.server.core.license;

import org.junit.jupiter.api.Test;

import java.security.*;
import java.security.spec.NamedParameterSpec;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Base64;

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

class LicenseValidatorTest {

    private KeyPair generateKeyPair() throws Exception {
        KeyPairGenerator kpg = KeyPairGenerator.getInstance("Ed25519");
        return kpg.generateKeyPair();
    }

    private String sign(PrivateKey key, String payload) throws Exception {
        Signature signer = Signature.getInstance("Ed25519");
        signer.initSign(key);
        signer.update(payload.getBytes());
        return Base64.getEncoder().encodeToString(signer.sign());
    }

    @Test
    void validate_validLicense_returnsLicenseInfo() throws Exception {
        KeyPair kp = generateKeyPair();
        String publicKeyBase64 = Base64.getEncoder().encodeToString(kp.getPublic().getEncoded());
        LicenseValidator validator = new LicenseValidator(publicKeyBase64);

        Instant expires = Instant.now().plus(365, ChronoUnit.DAYS);
        String payload = """
                {"tier":"HIGH","features":["topology","lineage","debugger"],"limits":{"max_agents":50,"retention_days":90},"iat":%d,"exp":%d}
                """.formatted(Instant.now().getEpochSecond(), expires.getEpochSecond()).trim();
        String signature = sign(kp.getPrivate(), payload);
        String token = Base64.getEncoder().encodeToString(payload.getBytes()) + "." + signature;

        LicenseInfo info = validator.validate(token);

        assertThat(info.tier()).isEqualTo("HIGH");
        assertThat(info.hasFeature(Feature.debugger)).isTrue();
        assertThat(info.hasFeature(Feature.replay)).isFalse();
        assertThat(info.getLimit("max_agents", 0)).isEqualTo(50);
        assertThat(info.isExpired()).isFalse();
    }

    @Test
    void validate_expiredLicense_throwsException() throws Exception {
        KeyPair kp = generateKeyPair();
        String publicKeyBase64 = Base64.getEncoder().encodeToString(kp.getPublic().getEncoded());
        LicenseValidator validator = new LicenseValidator(publicKeyBase64);

        Instant past = Instant.now().minus(1, ChronoUnit.DAYS);
        String payload = """
                {"tier":"LOW","features":["topology"],"limits":{},"iat":%d,"exp":%d}
                """.formatted(past.minus(30, ChronoUnit.DAYS).getEpochSecond(), past.getEpochSecond()).trim();
        String signature = sign(kp.getPrivate(), payload);
        String token = Base64.getEncoder().encodeToString(payload.getBytes()) + "." + signature;

        assertThatThrownBy(() -> validator.validate(token))
                .isInstanceOf(IllegalArgumentException.class)
                .hasMessageContaining("expired");
    }

    @Test
    void validate_tamperedPayload_throwsException() throws Exception {
        KeyPair kp = generateKeyPair();
        String publicKeyBase64 = Base64.getEncoder().encodeToString(kp.getPublic().getEncoded());
        LicenseValidator validator = new LicenseValidator(publicKeyBase64);

        String payload = """
                {"tier":"LOW","features":["topology"],"limits":{},"iat":0,"exp":9999999999}
                """.trim();
        String signature = sign(kp.getPrivate(), payload);

        // Tamper with payload
        String tampered = payload.replace("LOW", "BUSINESS");
        String token = Base64.getEncoder().encodeToString(tampered.getBytes()) + "." + signature;

        assertThatThrownBy(() -> validator.validate(token))
                .isInstanceOf(SecurityException.class)
                .hasMessageContaining("signature");
    }
}
