package kr.itn.itnhub.timeline;

import kr.itn.itnhub.AbstractDbTest;
import kr.itn.itnhub.feed.FileView;
import kr.itn.itnhub.mattermost.MattermostClient;
import kr.itn.itnhub.mattermost.MattermostException;
import kr.itn.itnhub.org.Organization;
import kr.itn.itnhub.org.OrganizationMapper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.web.servlet.MockMvc;

import java.util.List;

import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@AutoConfigureMockMvc
@WithMockUser(roles = "ADMIN")
class TimelineControllerTest extends AbstractDbTest {

    @Autowired
    MockMvc mvc;

    @Autowired
    OrganizationMapper mapper;

    @Autowired
    JdbcTemplate jdbc;

    @MockBean
    MattermostClient mattermost;

    private Long orgId;

    @BeforeEach
    void setUp() {
        jdbc.update("delete from organization");

        Organization org = new Organization();
        org.setOrgNo("001");
        org.setOrgName("국제방송교류재단");
        org.setChannelSlug("001");
        mapper.upsertBySeed(org);

        orgId = mapper.findAll().get(0).getId();
        mapper.updateChannelIdMj(orgId, "chan-mj");
        mapper.updateChannelIdLaw(orgId, "chan-law");
    }

    private void insertStageHistory(long orgId, int stage, long epochSeconds) {
        jdbc.update("insert into stage_history (org_id, stage, changed_at) values (?, ?, to_timestamp(?))",
                orgId, stage, epochSeconds);
    }

    private void insertMemo(long orgId, String contactName, String body, long epochSeconds) {
        jdbc.update("insert into work_memo (org_id, contact_name, body, created_at) "
                        + "values (?, ?, ?, to_timestamp(?))",
                orgId, contactName, body, epochSeconds);
    }

    @Test
    void 세_종류_이벤트를_최신순으로_합쳐서_돌려준다() throws Exception {
        insertStageHistory(orgId, 1, 1_000L);
        insertMemo(orgId, "송민지", "안녕하세요\n둘째줄입니다", 2_000L);

        when(mattermost.collectChannelFiles("chan-mj")).thenReturn(List.of(
                new FileView("f1", "보고서.pdf", 100L, "application/pdf", 3_000_000L, "박아이티")));
        when(mattermost.collectChannelFiles("chan-law")).thenReturn(List.of());

        mvc.perform(get("/api/orgs/{id}/timeline", orgId))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$", org.hamcrest.Matchers.hasSize(3)))
                .andExpect(jsonPath("$[0].type").value("FILE"))
                .andExpect(jsonPath("$[0].title").value("보고서.pdf"))
                .andExpect(jsonPath("$[0].detail").value("문정원 · 박아이티"))
                .andExpect(jsonPath("$[1].type").value("MEMO"))
                .andExpect(jsonPath("$[1].title").value("송민지"))
                .andExpect(jsonPath("$[1].detail").value("안녕하세요"))
                .andExpect(jsonPath("$[2].type").value("STAGE"))
                .andExpect(jsonPath("$[2].title").value("1"))
                .andExpect(jsonPath("$[2].detail").doesNotExist());
    }

    @Test
    void Mattermost_조회가_실패해도_단계와_메모_이벤트는_그대로_200으로_돌려준다() throws Exception {
        insertStageHistory(orgId, 2, 1_000L);
        insertMemo(orgId, "김문정", "메모내용", 2_000L);

        when(mattermost.collectChannelFiles("chan-mj")).thenThrow(new MattermostException("서버 오류"));
        when(mattermost.collectChannelFiles("chan-law")).thenThrow(new MattermostException("서버 오류"));

        mvc.perform(get("/api/orgs/{id}/timeline", orgId))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$", org.hamcrest.Matchers.hasSize(2)))
                .andExpect(jsonPath("$[0].type").value("MEMO"))
                .andExpect(jsonPath("$[1].type").value("STAGE"));
    }

    @Test
    void 존재하지_않는_기관이면_404다() throws Exception {
        long missingId = orgId + 999999L;

        mvc.perform(get("/api/orgs/{id}/timeline", missingId))
                .andExpect(status().isNotFound());
    }

    @Test
    void 기록이_없으면_빈_배열을_돌려준다() throws Exception {
        when(mattermost.collectChannelFiles("chan-mj")).thenReturn(List.of());
        when(mattermost.collectChannelFiles("chan-law")).thenReturn(List.of());

        mvc.perform(get("/api/orgs/{id}/timeline", orgId))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$", org.hamcrest.Matchers.hasSize(0)));
    }
}
