package com.pandoli365.bibimbap.security;

import com.pandoli365.bibimbap.mapper.JamEntriesMapper;
import com.pandoli365.bibimbap.mapper.JamJudgesMapper;
import org.junit.jupiter.api.Test;
import org.springframework.mock.web.MockHttpSession;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

/**
 * W2-2 잼 스코프 게이트 단위 테스트.
 * VP-2 (isJudge): 지정 유저 true / 미지정 false / 미인증 false.
 * VP-3 (isOwnEntry): 개인출품 / 팀멤버출품 / 타인출품 / 비활성 충돌 판정.
 * 전역 PermissionGate(권한 키 축)와 별도 축 — jam_judges / jam_entries 직접 조회 판정.
 */
class JamRoleGateTest {

    private static final long JAM_ID = 42L;
    private static final long GAME_ID = 88L;
    private static final long JUDGE_ID = 7L;

    private final JamJudgesMapper jamJudgesMapper = mock(JamJudgesMapper.class);
    private final JamEntriesMapper jamEntriesMapper = mock(JamEntriesMapper.class);
    private final JamRoleGate gate = new JamRoleGate(jamJudgesMapper, jamEntriesMapper);

    // ---- VP-2: isJudge(session, jamId) ----

    @Test
    void isJudgeReturnsTrueForAssignedUser() {
        // given: 세션 userId 보유 + jam_judges 에 (jamId, userId) 존재
        MockHttpSession session = sessionWithUser(JUDGE_ID);
        when(jamJudgesMapper.exists(JAM_ID, JUDGE_ID)).thenReturn(true);

        // when / then
        assertThat(gate.isJudge(session, JAM_ID)).isTrue();
        verify(jamJudgesMapper).exists(JAM_ID, JUDGE_ID);
    }

    @Test
    void isJudgeReturnsFalseForUnassignedUser() {
        // given: 세션 userId 보유하나 jam_judges 미등록
        MockHttpSession session = sessionWithUser(JUDGE_ID);
        when(jamJudgesMapper.exists(JAM_ID, JUDGE_ID)).thenReturn(false);

        // when / then
        assertThat(gate.isJudge(session, JAM_ID)).isFalse();
        verify(jamJudgesMapper).exists(JAM_ID, JUDGE_ID);
    }

    @Test
    void isJudgeReturnsFalseWhenUnauthenticated() {
        // given: userId 없는 빈 세션 — 미인증은 심사위원 아님
        MockHttpSession session = new MockHttpSession();

        // when / then: 미인증이면 jam_judges 조회조차 하지 않는다
        assertThat(gate.isJudge(session, JAM_ID)).isFalse();
        verify(jamJudgesMapper, never()).exists(anyLong(), anyLong());
    }

    @Test
    void isJudgeReturnsFalseForNullSession() {
        // given: 세션 자체가 null
        // when / then
        assertThat(gate.isJudge(null, JAM_ID)).isFalse();
        verify(jamJudgesMapper, never()).exists(anyLong(), anyLong());
    }

    // ---- VP-3: isOwnEntry(jamId, gameId, judgeUserId) ----
    // 게이트는 jam_entries 매퍼의 OR-EXISTS(개인 OR 팀멤버) 판정 결과를 그대로 전달한다.
    // 개인/팀/타인/비활성 SQL 분기 자체는 L2(dev DB contract) 소관 — 단위는 위임 정합을 본다.

    @Test
    void isOwnEntryTrueForPersonalEntry() {
        // given: 개인 출품(entrant_user_id == judge) → 매퍼 true
        when(jamEntriesMapper.isOwnEntry(JAM_ID, GAME_ID, JUDGE_ID)).thenReturn(true);

        // when / then
        assertThat(gate.isOwnEntry(JAM_ID, GAME_ID, JUDGE_ID)).isTrue();
        verify(jamEntriesMapper).isOwnEntry(JAM_ID, GAME_ID, JUDGE_ID);
    }

    @Test
    void isOwnEntryTrueForTeamMemberEntry() {
        // given: 팀 출품 + judge 가 그 팀 멤버(jam_team_members) → 매퍼 true
        when(jamEntriesMapper.isOwnEntry(JAM_ID, GAME_ID, JUDGE_ID)).thenReturn(true);

        // when / then
        assertThat(gate.isOwnEntry(JAM_ID, GAME_ID, JUDGE_ID)).isTrue();
        verify(jamEntriesMapper).isOwnEntry(JAM_ID, GAME_ID, JUDGE_ID);
    }

    @Test
    void isOwnEntryFalseForOthersEntry() {
        // given: 타인/타팀 출품 → 매퍼 false (충돌 아님 — 심사 가능)
        when(jamEntriesMapper.isOwnEntry(JAM_ID, GAME_ID, JUDGE_ID)).thenReturn(false);

        // when / then
        assertThat(gate.isOwnEntry(JAM_ID, GAME_ID, JUDGE_ID)).isFalse();
        verify(jamEntriesMapper).isOwnEntry(JAM_ID, GAME_ID, JUDGE_ID);
    }

    @Test
    void isOwnEntryFalseForInactiveEntry() {
        // given: 비활성(is_delete) 출품 → 매퍼 SQL 의 is_delete IS NOT TRUE 필터로 false
        when(jamEntriesMapper.isOwnEntry(JAM_ID, GAME_ID, JUDGE_ID)).thenReturn(false);

        // when / then
        assertThat(gate.isOwnEntry(JAM_ID, GAME_ID, JUDGE_ID)).isFalse();
        verify(jamEntriesMapper).isOwnEntry(JAM_ID, GAME_ID, JUDGE_ID);
    }

    // ---- helpers ----

    private MockHttpSession sessionWithUser(long userId) {
        MockHttpSession session = new MockHttpSession();
        session.setAttribute("userId", userId);
        return session;
    }
}
