package cloud.nstart.terravault.ordinis.restapi.auth;

import cloud.nstart.terravault.ordinis.restapi.error.OrdinisException;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;

import java.time.Instant;
import java.util.List;
import java.util.Map;

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

/**
 * Unit tests для {@link WorkflowRoles} — realm role extraction из JWT
 * claim {@code realm_access.roles} + enforcement через requireRole().
 */
class WorkflowRolesTest {

  private final WorkflowRoles roles = new WorkflowRoles();

  @AfterEach
  void clearSecurityContext() {
    SecurityContextHolder.clearContext();
  }

  @Test
  void anonymous_currentRoles_empty() {
    SecurityContextHolder.clearContext();
    assertThat(roles.currentRoles()).isEmpty();
  }

  @Test
  void anonymous_hasRole_false() {
    SecurityContextHolder.clearContext();
    assertThat(roles.hasRole(WorkflowRoles.SCHEMA_REVIEWER)).isFalse();
  }

  @Test
  void jwtWithApprover_hasRole_true() {
    setJwtWithRealmRoles(List.of(WorkflowRoles.SCHEMA_REVIEWER));
    assertThat(roles.hasRole(WorkflowRoles.SCHEMA_REVIEWER)).isTrue();
    assertThat(roles.hasRole(WorkflowRoles.SCHEMA_PUBLISHER)).isFalse();
  }

  @Test
  void jwtWithMultipleRoles_currentRoles_contains() {
    setJwtWithRealmRoles(List.of(
        WorkflowRoles.SCHEMA_REVIEWER,
        WorkflowRoles.SCHEMA_PUBLISHER,
        "ordinis:client:restricted",
        "some-other-role"));
    assertThat(roles.currentRoles())
        .containsExactlyInAnyOrder(
            WorkflowRoles.SCHEMA_REVIEWER,
            WorkflowRoles.SCHEMA_PUBLISHER,
            "ordinis:client:restricted",
            "some-other-role");
  }

  @Test
  void requireRole_present_noThrow() {
    setJwtWithRealmRoles(List.of(WorkflowRoles.SCHEMA_REVIEWER));
    roles.requireRole(WorkflowRoles.SCHEMA_REVIEWER);
    // no exception — passes
  }

  @Test
  void requireRole_missing_throws403() {
    setJwtWithRealmRoles(List.of("some-other-role"));
    assertThatThrownBy(() -> roles.requireRole(WorkflowRoles.SCHEMA_PUBLISHER))
        .isInstanceOf(OrdinisException.class)
        .hasMessageContaining(WorkflowRoles.SCHEMA_PUBLISHER);
  }

  @Test
  void requireRole_anonymous_throws() {
    SecurityContextHolder.clearContext();
    assertThatThrownBy(() -> roles.requireRole(WorkflowRoles.SCHEMA_REVIEWER))
        .isInstanceOf(OrdinisException.class);
  }

  @Test
  void jwtWithoutRealmAccess_emptyRoles() {
    setJwtWithClaims(Map.of("sub", "user-1")); // no realm_access claim
    assertThat(roles.currentRoles()).isEmpty();
  }

  @Test
  void jwtWithMalformedRealmAccess_emptyRoles() {
    setJwtWithClaims(Map.of(
        "sub", "user-1",
        "realm_access", "not-a-map")); // bad type — defensive parse
    assertThat(roles.currentRoles()).isEmpty();
  }

  // ─── helpers ─────────────────────────────────────────────────────────

  private void setJwtWithRealmRoles(List<String> realmRoles) {
    setJwtWithClaims(Map.of(
        "sub", "test-user",
        "realm_access", Map.of("roles", realmRoles)));
  }

  private void setJwtWithClaims(Map<String, Object> claims) {
    Jwt jwt = new Jwt(
        "test-token",
        Instant.now(),
        Instant.now().plusSeconds(600),
        Map.of("alg", "RS256"),
        claims);
    JwtAuthenticationToken auth = new JwtAuthenticationToken(jwt);
    auth.setAuthenticated(true);
    SecurityContextHolder.getContext().setAuthentication(auth);
  }
}
