package cn.nianxx.thhotel.platform.hotel.service.impl;

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

import cn.nianxx.thhotel.platform.hotel.common.enums.PlatformHotelStatus;
import cn.nianxx.thhotel.platform.hotel.domain.PlatformHotelEntity;
import cn.nianxx.thhotel.platform.hotel.repository.PlatformHotelRepository;
import cn.nianxx.thhotel.platform.hotel.service.HotelContextException;
import cn.nianxx.thhotel.platform.security.common.dto.AuthenticatedUserContext;
import cn.nianxx.thhotel.platform.security.service.CurrentUserContextService;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.http.HttpStatus;

@ExtendWith(MockitoExtension.class)
class HotelContextServiceImplTest {

    @Mock
    private PlatformHotelRepository hotelRepository;
    @Mock
    private CurrentUserContextService currentUserContextService;

    @Test
    void shouldResolveOnlyActiveHotelAsSystemHotel() {
        HotelContextServiceImpl service = service();
        when(hotelRepository.listActiveHotels()).thenReturn(List.of(activeHotel("HOTEL-ONLY")));

        assertThat(service.resolveSystemHotelId()).isEqualTo("HOTEL-ONLY");
    }

    @Test
    void shouldRejectMissingActiveSystemHotel() {
        HotelContextServiceImpl service = service();
        when(hotelRepository.listActiveHotels()).thenReturn(List.of());

        assertThatThrownBy(service::resolveSystemHotelId)
                .isInstanceOf(HotelContextException.class)
                .extracting("status", "errorCode")
                .containsExactly(HttpStatus.CONFLICT, "SYSTEM_HOTEL_NOT_CONFIGURED");
    }

    @Test
    void shouldRejectAmbiguousActiveSystemHotel() {
        HotelContextServiceImpl service = service();
        when(hotelRepository.listActiveHotels()).thenReturn(List.of(
                activeHotel("HOTEL-A"),
                activeHotel("HOTEL-B")));

        assertThatThrownBy(service::resolveSystemHotelId)
                .isInstanceOf(HotelContextException.class)
                .extracting("status", "errorCode")
                .containsExactly(HttpStatus.CONFLICT, "SYSTEM_HOTEL_AMBIGUOUS");
    }

    @Test
    void shouldUseCurrentUserDefaultHotelWhenRequestHotelMissing() {
        HotelContextServiceImpl service = service();
        when(currentUserContextService.currentUser()).thenReturn(Optional.of(userContext(
                "HOTEL-USER-DEFAULT",
                List.of("HOTEL-USER-DEFAULT", "HOTEL-OTHER"))));

        assertThat(service.resolveCurrentHotelId(null)).isEqualTo("HOTEL-USER-DEFAULT");
    }

    @Test
    void shouldRejectRequestedHotelOutsideCurrentUserAccessList() {
        HotelContextServiceImpl service = service();
        when(currentUserContextService.currentUser()).thenReturn(Optional.of(userContext(
                "HOTEL-USER-DEFAULT",
                List.of("HOTEL-USER-DEFAULT"))));

        assertThatThrownBy(() -> service.resolveCurrentHotelId("HOTEL-NOT-ALLOWED"))
                .isInstanceOf(HotelContextException.class)
                .extracting("status", "errorCode")
                .containsExactly(HttpStatus.FORBIDDEN, "HOTEL_ACCESS_DENIED");
    }

    @Test
    void shouldFallbackToSystemHotelWhenNoCurrentUser() {
        HotelContextServiceImpl service = service();
        when(currentUserContextService.currentUser()).thenReturn(Optional.empty());
        when(hotelRepository.listActiveHotels()).thenReturn(List.of(activeHotel("HOTEL-SYSTEM")));

        assertThat(service.resolveCurrentHotelId(null)).isEqualTo("HOTEL-SYSTEM");
    }

    private HotelContextServiceImpl service() {
        return new HotelContextServiceImpl(hotelRepository, currentUserContextService);
    }

    private PlatformHotelEntity activeHotel(String hotelId) {
        PlatformHotelEntity hotel = new PlatformHotelEntity();
        hotel.setHotelId(hotelId);
        hotel.setHotelName(hotelId);
        hotel.setHotelStatus(PlatformHotelStatus.ACTIVE.name());
        hotel.setTimeZone("Asia/Bangkok");
        hotel.setSortOrder(10);
        hotel.setCreatedAt(LocalDateTime.now());
        hotel.setUpdatedAt(LocalDateTime.now());
        return hotel;
    }

    private AuthenticatedUserContext userContext(String defaultHotelId, List<String> accessibleHotelIds) {
        return new AuthenticatedUserContext(
                1L,
                "tester",
                "测试用户",
                false,
                defaultHotelId,
                accessibleHotelIds,
                List.of());
    }
}
