package de.cannamanage.api.security;

import de.cannamanage.domain.entity.StaffAccount;
import de.cannamanage.domain.enums.StaffPermission;
import de.cannamanage.service.repository.StaffAccountRepository;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;

import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
class StaffPermissionCheckerTest {

    @Mock
    private StaffAccountRepository staffAccountRepository;

    @InjectMocks
    private StaffPermissionChecker checker;

    private UUID staffUserId;
    private StaffAccount staffAccount;

    @BeforeEach
    void setUp() {
        staffUserId = UUID.randomUUID();
        staffAccount = new StaffAccount();
        staffAccount.setUserId(staffUserId);
        staffAccount.setDisplayName("Test Staff");
        staffAccount.setActive(true);
        staffAccount.setGrantedPermissions(Set.of(
                StaffPermission.RECORD_DISTRIBUTION,
                StaffPermission.VIEW_MEMBER_LIST
        ));
    }

    @Test
    void adminAlwaysHasPermission() {
        Authentication auth = new UsernamePasswordAuthenticationToken(
                UUID.randomUUID(), null,
                List.of(new SimpleGrantedAuthority("ROLE_ADMIN"))
        );

        assertThat(checker.has(auth, StaffPermission.RECORD_DISTRIBUTION)).isTrue();
        assertThat(checker.has(auth, StaffPermission.MANAGE_GROW_CALENDAR)).isTrue();
    }

    @Test
    void staffWithGrantedPermission_returnsTrue() {
        Authentication auth = new UsernamePasswordAuthenticationToken(
                staffUserId, null,
                List.of(new SimpleGrantedAuthority("ROLE_STAFF"))
        );

        when(staffAccountRepository.findByUserId(staffUserId)).thenReturn(Optional.of(staffAccount));

        assertThat(checker.has(auth, StaffPermission.RECORD_DISTRIBUTION)).isTrue();
    }

    @Test
    void staffWithoutGrantedPermission_returnsFalse() {
        Authentication auth = new UsernamePasswordAuthenticationToken(
                staffUserId, null,
                List.of(new SimpleGrantedAuthority("ROLE_STAFF"))
        );

        when(staffAccountRepository.findByUserId(staffUserId)).thenReturn(Optional.of(staffAccount));

        assertThat(checker.has(auth, StaffPermission.MANAGE_GROW_CALENDAR)).isFalse();
    }

    @Test
    void inactiveStaff_returnsFalse() {
        staffAccount.setActive(false);
        Authentication auth = new UsernamePasswordAuthenticationToken(
                staffUserId, null,
                List.of(new SimpleGrantedAuthority("ROLE_STAFF"))
        );

        when(staffAccountRepository.findByUserId(staffUserId)).thenReturn(Optional.of(staffAccount));

        assertThat(checker.has(auth, StaffPermission.RECORD_DISTRIBUTION)).isFalse();
    }

    @Test
    void memberRole_returnsFalse() {
        Authentication auth = new UsernamePasswordAuthenticationToken(
                UUID.randomUUID(), null,
                List.of(new SimpleGrantedAuthority("ROLE_MEMBER"))
        );

        assertThat(checker.has(auth, StaffPermission.VIEW_MEMBER_LIST)).isFalse();
    }

    @Test
    void nullAuthentication_returnsFalse() {
        assertThat(checker.has(null, StaffPermission.RECORD_DISTRIBUTION)).isFalse();
    }

    @Test
    void staffWithNoAccount_returnsFalse() {
        Authentication auth = new UsernamePasswordAuthenticationToken(
                staffUserId, null,
                List.of(new SimpleGrantedAuthority("ROLE_STAFF"))
        );

        when(staffAccountRepository.findByUserId(staffUserId)).thenReturn(Optional.empty());

        assertThat(checker.has(auth, StaffPermission.RECORD_DISTRIBUTION)).isFalse();
    }
}
