package dev.lions.unionflow.server.entity;

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

import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.UUID;

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

@DisplayName("ApproverAction")
class ApproverActionTest {

    private static TransactionApproval newApproval() {
        TransactionApproval ta = new TransactionApproval();
        ta.setId(UUID.randomUUID());
        return ta;
    }

    // -------------------------------------------------------------------------
    // approve
    // -------------------------------------------------------------------------

    @Test
    @DisplayName("approve: positionne la décision à APPROVED")
    void approve_setsDecisionApproved() {
        ApproverAction a = new ApproverAction();
        a.setDecision("PENDING");
        a.approve("Tout est correct");
        assertThat(a.getDecision()).isEqualTo("APPROVED");
    }

    @Test
    @DisplayName("approve: positionne le commentaire")
    void approve_setsComment() {
        ApproverAction a = new ApproverAction();
        a.approve("Approuvé sans réserve");
        assertThat(a.getComment()).isEqualTo("Approuvé sans réserve");
    }

    @Test
    @DisplayName("approve: positionne decidedAt à une date non nulle")
    void approve_setsDecidedAt() {
        LocalDateTime before = LocalDateTime.now().minusSeconds(1);
        ApproverAction a = new ApproverAction();
        a.approve("OK");
        assertThat(a.getDecidedAt()).isNotNull().isAfterOrEqualTo(before);
    }

    // -------------------------------------------------------------------------
    // reject
    // -------------------------------------------------------------------------

    @Test
    @DisplayName("reject: positionne la décision à REJECTED")
    void reject_setsDecisionRejected() {
        ApproverAction a = new ApproverAction();
        a.setDecision("PENDING");
        a.reject("Montant trop élevé");
        assertThat(a.getDecision()).isEqualTo("REJECTED");
    }

    @Test
    @DisplayName("reject: positionne la raison dans le commentaire")
    void reject_setsReason() {
        ApproverAction a = new ApproverAction();
        a.reject("Justificatif manquant");
        assertThat(a.getComment()).isEqualTo("Justificatif manquant");
    }

    @Test
    @DisplayName("reject: positionne decidedAt à une date non nulle")
    void reject_setsDecidedAt() {
        LocalDateTime before = LocalDateTime.now().minusSeconds(1);
        ApproverAction a = new ApproverAction();
        a.reject("Raison");
        assertThat(a.getDecidedAt()).isNotNull().isAfterOrEqualTo(before);
    }

    // -------------------------------------------------------------------------
    // onCreate (réflexion)
    // -------------------------------------------------------------------------

    @Test
    @DisplayName("onCreate: initialise decision à PENDING si null")
    void onCreate_initializesDecisionIfNull() throws Exception {
        ApproverAction a = new ApproverAction();
        a.setDecision(null);

        Method onCreate = ApproverAction.class.getDeclaredMethod("onCreate");
        onCreate.setAccessible(true);
        onCreate.invoke(a);

        assertThat(a.getDecision()).isEqualTo("PENDING");
    }

    @Test
    @DisplayName("onCreate: ne remplace pas decision si déjà renseigné")
    void onCreate_doesNotOverrideDecision() throws Exception {
        ApproverAction a = new ApproverAction();
        a.setDecision("APPROVED");

        Method onCreate = ApproverAction.class.getDeclaredMethod("onCreate");
        onCreate.setAccessible(true);
        onCreate.invoke(a);

        assertThat(a.getDecision()).isEqualTo("APPROVED");
    }

    // -------------------------------------------------------------------------
    // builder
    // -------------------------------------------------------------------------

    @Test
    @DisplayName("builder: positionne tous les champs correctement")
    void builder_setsAllFields() {
        TransactionApproval approval = newApproval();
        UUID approverId = UUID.randomUUID();
        LocalDateTime decidedAt = LocalDateTime.of(2026, 3, 20, 14, 30);

        ApproverAction a = ApproverAction.builder()
            .approval(approval)
            .approverId(approverId)
            .approverName("Mamadou Diallo")
            .approverRole("TRESORIER")
            .decision("APPROVED")
            .comment("Dépense justifiée")
            .decidedAt(decidedAt)
            .build();

        assertThat(a.getApproval()).isSameAs(approval);
        assertThat(a.getApproverId()).isEqualTo(approverId);
        assertThat(a.getApproverName()).isEqualTo("Mamadou Diallo");
        assertThat(a.getApproverRole()).isEqualTo("TRESORIER");
        assertThat(a.getDecision()).isEqualTo("APPROVED");
        assertThat(a.getComment()).isEqualTo("Dépense justifiée");
        assertThat(a.getDecidedAt()).isEqualTo(decidedAt);
    }

    // -------------------------------------------------------------------------
    // getters / setters
    // -------------------------------------------------------------------------

    @Test
    @DisplayName("getters/setters: tous les champs accessibles en lecture/écriture")
    void gettersSetters_workCorrectly() {
        TransactionApproval approval = newApproval();
        UUID approverId = UUID.randomUUID();
        LocalDateTime decidedAt = LocalDateTime.now();

        ApproverAction a = new ApproverAction();
        a.setApproval(approval);
        a.setApproverId(approverId);
        a.setApproverName("Aïssata Koné");
        a.setApproverRole("VICE_PRESIDENT");
        a.setDecision("REJECTED");
        a.setComment("Documents insuffisants");
        a.setDecidedAt(decidedAt);

        assertThat(a.getApproval()).isSameAs(approval);
        assertThat(a.getApproverId()).isEqualTo(approverId);
        assertThat(a.getApproverName()).isEqualTo("Aïssata Koné");
        assertThat(a.getApproverRole()).isEqualTo("VICE_PRESIDENT");
        assertThat(a.getDecision()).isEqualTo("REJECTED");
        assertThat(a.getComment()).isEqualTo("Documents insuffisants");
        assertThat(a.getDecidedAt()).isEqualTo(decidedAt);
    }
}
