package de.effigenix.application.usermanagement;

import de.effigenix.application.usermanagement.dto.UserDTO;
import de.effigenix.domain.usermanagement.*;
import de.effigenix.shared.common.Result;
import de.effigenix.shared.security.ActorId;
import de.effigenix.shared.security.AuthorizationPort;
import de.effigenix.shared.security.BranchId;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.HashSet;
import java.util.List;

import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
@DisplayName("ListUsers Use Case")
class ListUsersTest {

    @Mock private UserRepository userRepository;
    @Mock private AuthorizationPort authPort;

    private ListUsers listUsers;
    private ActorId performedBy;
    private User user1;
    private User user2;

    @BeforeEach
    void setUp() {
        listUsers = new ListUsers(userRepository, authPort);
        performedBy = ActorId.of("admin-user");
        user1 = User.reconstitute(
                UserId.of("user-1"), "john.doe", "john@example.com",
                new PasswordHash("$2a$12$R9h/cIPz0gi.URNN3kh2OPST9EBwVeL00lzQRYe3z08MZx3e8YCWW"),
                new HashSet<>(), "branch-1", UserStatus.ACTIVE, OffsetDateTime.now(ZoneOffset.UTC), null
        );
        user2 = User.reconstitute(
                UserId.of("user-2"), "jane.doe", "jane@example.com",
                new PasswordHash("$2a$12$R9h/cIPz0gi.URNN3kh2OPST9EBwVeL00lzQRYe3z08MZx3e8YCWW"),
                new HashSet<>(), "branch-1", UserStatus.ACTIVE, OffsetDateTime.now(ZoneOffset.UTC), null
        );
    }

    @Test
    @DisplayName("should_ReturnAllUsers_When_Authorized")
    void should_ReturnAllUsers_When_Authorized() {
        when(authPort.can(performedBy, UserManagementAction.USER_LIST)).thenReturn(true);
        when(userRepository.findAll()).thenReturn(Result.success(List.of(user1, user2)));

        Result<UserError, List<UserDTO>> result = listUsers.execute(performedBy);

        assertThat(result.isSuccess()).isTrue();
        assertThat(result.unsafeGetValue()).hasSize(2);
        assertThat(result.unsafeGetValue()).extracting(UserDTO::username)
                .containsExactlyInAnyOrder("john.doe", "jane.doe");
    }

    @Test
    @DisplayName("should_ReturnEmptyList_When_NoUsersExist")
    void should_ReturnEmptyList_When_NoUsersExist() {
        when(authPort.can(performedBy, UserManagementAction.USER_LIST)).thenReturn(true);
        when(userRepository.findAll()).thenReturn(Result.success(List.of()));

        Result<UserError, List<UserDTO>> result = listUsers.execute(performedBy);

        assertThat(result.isSuccess()).isTrue();
        assertThat(result.unsafeGetValue()).isEmpty();
    }

    @Test
    @DisplayName("should_FailWithUnauthorized_When_ActorLacksPermission")
    void should_FailWithUnauthorized_When_ActorLacksPermission() {
        when(authPort.can(performedBy, UserManagementAction.USER_LIST)).thenReturn(false);

        Result<UserError, List<UserDTO>> result = listUsers.execute(performedBy);

        assertThat(result.isFailure()).isTrue();
        assertThat(result.unsafeGetError()).isInstanceOf(UserError.Unauthorized.class);
        verify(userRepository, never()).findAll();
    }

    @Test
    @DisplayName("should_ReturnBranchUsers_When_FilteredByBranch")
    void should_ReturnBranchUsers_When_FilteredByBranch() {
        when(authPort.can(performedBy, UserManagementAction.USER_LIST)).thenReturn(true);
        when(userRepository.findByBranchId("branch-1")).thenReturn(Result.success(List.of(user1, user2)));

        Result<UserError, List<UserDTO>> result = listUsers.executeForBranch(BranchId.of("branch-1"), performedBy);

        assertThat(result.isSuccess()).isTrue();
        assertThat(result.unsafeGetValue()).hasSize(2);
    }

    @Test
    @DisplayName("should_FailWithUnauthorized_When_ActorLacksPermissionForBranchList")
    void should_FailWithUnauthorized_When_ActorLacksPermissionForBranchList() {
        when(authPort.can(performedBy, UserManagementAction.USER_LIST)).thenReturn(false);

        Result<UserError, List<UserDTO>> result = listUsers.executeForBranch(BranchId.of("branch-1"), performedBy);

        assertThat(result.isFailure()).isTrue();
        assertThat(result.unsafeGetError()).isInstanceOf(UserError.Unauthorized.class);
        verify(userRepository, never()).findByBranchId(anyString());
    }
}
