package dev.lions.unionflow.server.service;

import dev.lions.unionflow.server.entity.Permission;
import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.TestTransaction;
import jakarta.inject.Inject;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import java.util.List;
import java.util.UUID;

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

@QuarkusTest
class PermissionServiceTest {

    @Inject
    PermissionService permissionService;

    @Test
    @TestTransaction
    @DisplayName("trouverParId avec UUID inexistant retourne null")
    void trouverParId_inexistant_returnsNull() {
        Permission found = permissionService.trouverParId(UUID.randomUUID());
        assertThat(found).isNull();
    }

    @Test
    @TestTransaction
    @DisplayName("trouverParCode avec code inexistant retourne null")
    void trouverParCode_inexistant_returnsNull() {
        Permission found = permissionService.trouverParCode("CODE_INEXISTANT_" + UUID.randomUUID());
        assertThat(found).isNull();
    }

    @Test
    @TestTransaction
    @DisplayName("listerToutesActives retourne une liste")
    void listerToutesActives_returnsList() {
        List<Permission> list = permissionService.listerToutesActives();
        assertThat(list).isNotNull();
    }

    @Test
    @TestTransaction
    @DisplayName("listerParModule retourne une liste")
    void listerParModule_returnsList() {
        List<Permission> list = permissionService.listerParModule("TEST_MODULE");
        assertThat(list).isNotNull();
    }

    @Test
    @TestTransaction
    @DisplayName("listerParRessource retourne une liste")
    void listerParRessource_returnsList() {
        List<Permission> list = permissionService.listerParRessource("TEST_RESSOURCE");
        assertThat(list).isNotNull();
    }

    @Test
    @TestTransaction
    @DisplayName("creerPermission avec code unique crée la permission")
    void creerPermission_codeUnique_createsPermission() {
        String code = "TEST_PERM_" + UUID.randomUUID().toString().substring(0, 8);
        Permission perm = Permission.builder()
                .code(code)
                .module("TEST")
                .ressource("SERVICE")
                .action("READ")
                .libelle("Permission test")
                .build();
        Permission created = permissionService.creerPermission(perm);
        assertThat(created).isNotNull();
        assertThat(created.getId()).isNotNull();
        assertThat(created.getCode()).isEqualTo(code);
    }

    @Test
    @TestTransaction
    @DisplayName("creerPermission avec code déjà existant lance IllegalArgumentException")
    void creerPermission_codeExistant_throws() {
        String code = "PERM_DUP_" + UUID.randomUUID().toString().substring(0, 8);
        Permission perm = Permission.builder()
                .code(code)
                .module("TEST")
                .ressource("DUP")
                .action("READ")
                .libelle("Première")
                .build();
        permissionService.creerPermission(perm);
        Permission duplicate = Permission.builder()
                .code(code)
                .module("TEST")
                .ressource("DUP")
                .action("WRITE")
                .libelle("Duplicate")
                .build();
        assertThatThrownBy(() -> permissionService.creerPermission(duplicate))
                .isInstanceOf(IllegalArgumentException.class)
                .hasMessageContaining("existe déjà");
    }

    @Test
    @TestTransaction
    @DisplayName("creerPermission sans code génère le code depuis module/ressource/action")
    void creerPermission_sansCode_generateCode() {
        Permission perm = Permission.builder()
                .code("") // vide → génération automatique
                .module("MODX")
                .ressource("RESX")
                .action("READ")
                .libelle("Permission auto-code")
                .build();
        Permission created = permissionService.creerPermission(perm);
        assertThat(created).isNotNull();
        assertThat(created.getCode()).contains("MODX");
        assertThat(created.getCode()).contains("RESX");
        assertThat(created.getCode()).contains("READ");
    }

    @Test
    @TestTransaction
    @DisplayName("creerPermission avec code null génère le code depuis module/ressource/action (L47 code==null true)")
    void creerPermission_codeNull_generateCode() {
        // Couvre L47 branche: code == null → true (short-circuit) → generateCode
        Permission perm = Permission.builder()
                .code(null) // null → L47: code == null → true → generate code
                .module("MODNULL")
                .ressource("RESNULL")
                .action("DELETE")
                .libelle("Permission code null")
                .build();
        Permission created = permissionService.creerPermission(perm);
        assertThat(created).isNotNull();
        assertThat(created.getCode()).isNotNull().isNotEmpty();
    }

    @Test
    @TestTransaction
    @DisplayName("mettreAJourPermission avec ID inexistant lance NotFoundException")
    void mettreAJourPermission_inexistant_throwsNotFound() {
        Permission modification = Permission.builder()
                .code("PERM_MOD_X")
                .module("MOD")
                .ressource("RES")
                .action("UPDATE")
                .libelle("Modifiée")
                .build();
        assertThatThrownBy(() -> permissionService.mettreAJourPermission(UUID.randomUUID(), modification))
                .isInstanceOf(jakarta.ws.rs.NotFoundException.class);
    }

    @Test
    @TestTransaction
    @DisplayName("mettreAJourPermission met à jour les champs correctement")
    void mettreAJourPermission_found_updatesPermission() {
        String code = "PERM_UPD_" + UUID.randomUUID().toString().substring(0, 8);
        Permission perm = Permission.builder()
                .code(code)
                .module("MOD_INIT")
                .ressource("RES_INIT")
                .action("READ")
                .libelle("Libellé initial")
                .build();
        Permission created = permissionService.creerPermission(perm);

        String newCode = "PERM_UPD2_" + UUID.randomUUID().toString().substring(0, 8);
        Permission modification = Permission.builder()
                .code(newCode)
                .module("MOD_UPD")
                .ressource("RES_UPD")
                .action("WRITE")
                .libelle("Libellé mis à jour")
                .build();
        Permission updated = permissionService.mettreAJourPermission(created.getId(), modification);
        assertThat(updated.getModule()).isEqualTo("MOD_UPD");
        assertThat(updated.getAction()).isEqualTo("WRITE");
        assertThat(updated.getLibelle()).isEqualTo("Libellé mis à jour");
    }

    @Test
    @TestTransaction
    @DisplayName("supprimerPermission avec ID inexistant lance NotFoundException")
    void supprimerPermission_inexistant_throwsNotFound() {
        assertThatThrownBy(() -> permissionService.supprimerPermission(UUID.randomUUID()))
                .isInstanceOf(jakarta.ws.rs.NotFoundException.class);
    }

    @Test
    @TestTransaction
    @DisplayName("supprimerPermission désactive la permission")
    void supprimerPermission_found_desactivesPermission() {
        String code = "PERM_DEL_" + UUID.randomUUID().toString().substring(0, 8);
        Permission perm = Permission.builder()
                .code(code)
                .module("MOD_DEL")
                .ressource("RES_DEL")
                .action("DELETE")
                .libelle("À supprimer")
                .build();
        Permission created = permissionService.creerPermission(perm);
        permissionService.supprimerPermission(created.getId());
        // findPermissionById filtre sur actif=true, donc ne trouve plus la permission
        Permission found = permissionService.trouverParId(created.getId());
        assertThat(found).isNull();
    }
}
