package dev.lions.unionflow.server.service.compliance;

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

import java.lang.reflect.Field;
import java.util.UUID;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

class KpiShareTokenServiceTest {

  private KpiShareTokenService service;

  @BeforeEach
  void setUp() throws Exception {
    service = new KpiShareTokenService();
    Field f = KpiShareTokenService.class.getDeclaredField("secret");
    f.setAccessible(true);
    f.set(service, "test-secret-32-chars-minimum-okayyyy");
  }

  @Test
  @DisplayName("Round-trip — token généré pour orgId est vérifiable et retourne le même orgId")
  void roundtrip() {
    UUID orgId = UUID.randomUUID();
    String token = service.generer(orgId, 3600);
    assertThat(token).isNotBlank();
    assertThat(token.split("\\.")).hasSize(3);

    var result = service.verifier(token);
    assertThat(result).contains(orgId);
  }

  @Test
  @DisplayName("ttlSeconds<=0 → applique défaut 7 jours")
  void ttlDefault() {
    UUID orgId = UUID.randomUUID();
    String token = service.generer(orgId, 0);
    assertThat(service.verifier(token)).contains(orgId);
    String token2 = service.generer(orgId, -1);
    assertThat(service.verifier(token2)).contains(orgId);
  }

  @Test
  @DisplayName("organisationId null → IllegalArgumentException")
  void orgIdNull() {
    org.junit.jupiter.api.Assertions.assertThrows(
        IllegalArgumentException.class,
        () -> service.generer(null, 3600));
  }

  @Test
  @DisplayName("Token expiré (ttl 0 secondes effectives) → empty")
  void expired() throws Exception {
    UUID orgId = UUID.randomUUID();
    // Bricolage : générer un token avec timestamp dans le passé via signature manuelle
    // Plus simple : utiliser ttl 0 puis attendre 100ms... mais flaky.
    // Approche : on appelle generer avec ttl très court (1ms équivalent),
    // mais en réalité ttl est en secondes et ne peut être < 1 dans cette API.
    // On force via reflection : construire un token expiré manuellement
    String orgEnc = KpiShareTokenService.encode(orgId.toString());
    String pastExp = KpiShareTokenService.encode(Long.toString(System.currentTimeMillis() - 1000));
    // Pas de signature valide → vérifier que ça retourne empty (signature mismatch)
    String fakeToken = orgEnc + "." + pastExp + "." + KpiShareTokenService.encode("fake-sig");
    assertThat(service.verifier(fakeToken)).isEmpty();
  }

  @Test
  @DisplayName("Token avec signature invalide → empty")
  void tampering() {
    UUID orgId = UUID.randomUUID();
    String valid = service.generer(orgId, 3600);
    String[] parts = valid.split("\\.");
    // Modifier le 3e part (signature)
    String tampered = parts[0] + "." + parts[1] + "." + KpiShareTokenService.encode("forged");
    assertThat(service.verifier(tampered)).isEmpty();
  }

  @Test
  @DisplayName("Token mal formé (< 3 parts) → empty")
  void malformed() {
    assertThat(service.verifier("oneparts")).isEmpty();
    assertThat(service.verifier("two.parts")).isEmpty();
    assertThat(service.verifier("a.b.c.d.e")).isEmpty();
  }

  @Test
  @DisplayName("Token null/blank → empty")
  void blanks() {
    assertThat(service.verifier(null)).isEmpty();
    assertThat(service.verifier("")).isEmpty();
    assertThat(service.verifier("   ")).isEmpty();
  }

  @Test
  @DisplayName("Token modifié sur orgId → signature ne valide plus")
  void tamperedOrgId() {
    UUID orgId = UUID.randomUUID();
    String valid = service.generer(orgId, 3600);
    String[] parts = valid.split("\\.");
    // Substituer un autre orgId encodé sans recalculer signature
    String otherOrg = KpiShareTokenService.encode(UUID.randomUUID().toString());
    String tampered = otherOrg + "." + parts[1] + "." + parts[2];
    assertThat(service.verifier(tampered)).isEmpty();
  }

  @Test
  @DisplayName("Encode/decode round-trip")
  void encodeDecodeRoundtrip() {
    String s = "Test 123 éàù";
    assertThat(KpiShareTokenService.decode(KpiShareTokenService.encode(s))).isEqualTo(s);
  }
}
