package com.cameleer.server.core.rbac;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.util.*;

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

class ClaimMappingServiceTest {

    private ClaimMappingService service;

    @BeforeEach
    void setUp() {
        service = new ClaimMappingService();
    }

    @Test
    void evaluate_containsMatch_onStringArrayClaim() {
        var rule = new ClaimMappingRule(
                UUID.randomUUID(), "groups", "contains", "cameleer-admins",
                "assignRole", "ADMIN", 0, null);

        Map<String, Object> claims = Map.of("groups", List.of("eng", "cameleer-admins", "devops"));

        var results = service.evaluate(List.of(rule), claims);

        assertThat(results).hasSize(1);
        assertThat(results.get(0).rule()).isEqualTo(rule);
    }

    @Test
    void evaluate_equalsMatch_onStringClaim() {
        var rule = new ClaimMappingRule(
                UUID.randomUUID(), "department", "equals", "platform",
                "assignRole", "OPERATOR", 0, null);

        Map<String, Object> claims = Map.of("department", "platform");

        var results = service.evaluate(List.of(rule), claims);

        assertThat(results).hasSize(1);
    }

    @Test
    void evaluate_regexMatch() {
        var rule = new ClaimMappingRule(
                UUID.randomUUID(), "email", "regex", ".*@example\\.com$",
                "addToGroup", "Example Corp", 0, null);

        Map<String, Object> claims = Map.of("email", "john@example.com");

        var results = service.evaluate(List.of(rule), claims);

        assertThat(results).hasSize(1);
    }

    @Test
    void evaluate_noMatch_returnsEmpty() {
        var rule = new ClaimMappingRule(
                UUID.randomUUID(), "groups", "contains", "cameleer-admins",
                "assignRole", "ADMIN", 0, null);

        Map<String, Object> claims = Map.of("groups", List.of("eng", "devops"));

        var results = service.evaluate(List.of(rule), claims);

        assertThat(results).isEmpty();
    }

    @Test
    void evaluate_missingClaim_returnsEmpty() {
        var rule = new ClaimMappingRule(
                UUID.randomUUID(), "groups", "contains", "admins",
                "assignRole", "ADMIN", 0, null);

        Map<String, Object> claims = Map.of("department", "eng");

        var results = service.evaluate(List.of(rule), claims);

        assertThat(results).isEmpty();
    }

    @Test
    void evaluate_rulesOrderedByPriority() {
        var lowPriority = new ClaimMappingRule(
                UUID.randomUUID(), "role", "equals", "dev",
                "assignRole", "VIEWER", 0, null);
        var highPriority = new ClaimMappingRule(
                UUID.randomUUID(), "role", "equals", "dev",
                "assignRole", "OPERATOR", 10, null);

        Map<String, Object> claims = Map.of("role", "dev");

        var results = service.evaluate(List.of(highPriority, lowPriority), claims);

        assertThat(results).hasSize(2);
        assertThat(results.get(0).rule().priority()).isEqualTo(0);
        assertThat(results.get(1).rule().priority()).isEqualTo(10);
    }

    @Test
    void evaluate_containsMatch_onSpaceSeparatedString() {
        var rule = new ClaimMappingRule(
                UUID.randomUUID(), "scope", "contains", "server:admin",
                "assignRole", "ADMIN", 0, null);

        Map<String, Object> claims = Map.of("scope", "openid profile server:admin");

        var results = service.evaluate(List.of(rule), claims);

        assertThat(results).hasSize(1);
    }
}
