package com.pandoli365.bibimbap.controller;

import com.pandoli365.bibimbap.data.JamAwardData;
import com.pandoli365.bibimbap.data.JamData;
import com.pandoli365.bibimbap.mapper.JamAwardsMapper;
import com.pandoli365.bibimbap.mapper.JamsMapper;
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.ui.ExtendedModelMap;
import org.springframework.ui.Model;

import java.util.List;
import java.util.Map;

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

/**
 * W2-3 시상 결과 페이지 컨트롤러 단위 테스트 (plain Mockito, MockMvc 미사용).
 *
 * <p>results 노출 게이트(잼 없음/은닉 → /jams 리다이렉트)와 awardTrack 별 그룹핑
 * (byTrack LinkedHashMap + grand 파생)을 검증한다. results 는 세션 인자가 없는
 * 공개 GET 이므로 익명 접근 시에도 잼 가시 시 jam-results 뷰를 반환한다.
 */
@ExtendWith(MockitoExtension.class)
class JamAwardControllerTest {

    private static final long JAM_ID = 42L;
    private static final String SLUG = "my-jam";

    @Mock
    private JamsMapper jamsMapper;

    @Mock
    private JamAwardsMapper jamAwardsMapper;

    /** 잼 없음 → /jams 리다이렉트, 시상 조회 전 차단. */
    @Test
    void resultsRedirectsWhenJamMissing() {
        JamAwardController controller = controller();
        Model model = new ExtendedModelMap();
        when(jamsMapper.getBySlug(SLUG)).thenReturn(null);

        String view = controller.results(SLUG, model);

        assertThat(view).isEqualTo("redirect:/jams");
        verify(jamAwardsMapper, never()).listByJamWithGame(anyLong());
    }

    /** 은닉 잼(isVisible=false) → /jams 리다이렉트, 시상 조회 전 차단. */
    @Test
    void resultsRedirectsWhenJamHidden() {
        JamAwardController controller = controller();
        Model model = new ExtendedModelMap();
        when(jamsMapper.getBySlug(SLUG)).thenReturn(hiddenJam());

        String view = controller.results(SLUG, model);

        assertThat(view).isEqualTo("redirect:/jams");
        verify(jamAwardsMapper, never()).listByJamWithGame(anyLong());
    }

    /** 가시 잼 + 미산정(빈 시상) → jam-results, byTrack 빈 Map / grand 빈 List. */
    @Test
    void resultsRendersEmptyWhenNoAwards() {
        JamAwardController controller = controller();
        Model model = new ExtendedModelMap();
        when(jamsMapper.getBySlug(SLUG)).thenReturn(visibleJam());
        when(jamAwardsMapper.listByJamWithGame(JAM_ID)).thenReturn(List.of());

        String view = controller.results(SLUG, model);

        assertThat(view).isEqualTo("jam-results");
        assertThat(byTrack(model)).isEmpty();
        assertThat((List<?>) model.getAttribute("grand")).isEmpty();
    }

    /** 가시 잼 + 다트랙 시상 → awardTrack 별 그룹핑, grand 는 GRAND 트랙 파생. */
    @Test
    void resultsGroupsByTrack() {
        JamAwardController controller = controller();
        Model model = new ExtendedModelMap();
        when(jamsMapper.getBySlug(SLUG)).thenReturn(visibleJam());
        when(jamAwardsMapper.listByJamWithGame(JAM_ID)).thenReturn(List.of(
                award("JUDGE", 1, 7L),
                award("JUDGE", 2, 8L),
                award("GRAND", 1, 7L),
                award("POPULAR", 1, 9L)
        ));

        String view = controller.results(SLUG, model);

        assertThat(view).isEqualTo("jam-results");
        Map<String, List<JamAwardData>> byTrack = byTrack(model);
        assertThat(byTrack).containsOnlyKeys("JUDGE", "GRAND", "POPULAR");
        assertThat(byTrack.get("JUDGE")).hasSize(2);
        assertThat(byTrack.get("GRAND")).hasSize(1);
        assertThat(byTrack.get("POPULAR")).hasSize(1);
        assertThat((List<?>) model.getAttribute("grand")).hasSize(1);
    }

    /** results 는 세션 인자 없음 — 미로그인도 가시 잼 시 jam-results 뷰 반환. */
    @Test
    void resultsAccessibleWhenAnonymous() {
        JamAwardController controller = controller();
        Model model = new ExtendedModelMap();
        when(jamsMapper.getBySlug(SLUG)).thenReturn(visibleJam());
        when(jamAwardsMapper.listByJamWithGame(JAM_ID)).thenReturn(List.of());

        String view = controller.results(SLUG, model);

        assertThat(view).isEqualTo("jam-results");
        assertThat(model.getAttribute("jam")).isNotNull();
    }

    // ==== helpers ====

    private JamAwardController controller() {
        return new JamAwardController(jamsMapper, jamAwardsMapper);
    }

    @SuppressWarnings("unchecked")
    private Map<String, List<JamAwardData>> byTrack(Model model) {
        return (Map<String, List<JamAwardData>>) model.getAttribute("byTrack");
    }

    private JamData visibleJam() {
        JamData jam = new JamData();
        jam.setId(JAM_ID);
        jam.setSlug(SLUG);
        jam.setIsVisible(true);
        return jam;
    }

    private JamData hiddenJam() {
        JamData jam = new JamData();
        jam.setId(JAM_ID);
        jam.setSlug(SLUG);
        jam.setIsVisible(false);
        return jam;
    }

    private JamAwardData award(String track, int rank, long gameId) {
        JamAwardData a = new JamAwardData();
        a.setAwardTrack(track);
        a.setRank(rank);
        a.setGameId(gameId);
        a.setGameName("게임" + gameId);
        return a;
    }
}
