package kr.itn.itnhub.feed;

import kr.itn.itnhub.AbstractDbTest;
import kr.itn.itnhub.mattermost.MattermostClient;
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.http.HttpHeaders;
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.ArgumentMatchers.eq;
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.header;
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 ChannelFeedControllerTest 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");
    }

    @Test
    void 게시물_조회는_문정원_채널로_라우팅한다() throws Exception {
        when(mattermost.getRecentPosts(eq("chan-mj"), eq(60))).thenReturn(List.of(
                new PostView("p1", "송민지", "안녕하세요", 100L, false, List.of())));

        mvc.perform(get("/api/orgs/{id}/posts", orgId).param("channel", "mj"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$[0].id").value("p1"))
                .andExpect(jsonPath("$[0].user").value("송민지"))
                .andExpect(jsonPath("$[0].message").value("안녕하세요"));
    }

    @Test
    void 게시물_조회는_law_파라미터면_법률검토_채널로_라우팅한다() throws Exception {
        when(mattermost.getRecentPosts(eq("chan-law"), eq(60))).thenReturn(List.of(
                new PostView("p2", "변호사", "검토합니다", 200L, false, List.of())));

        mvc.perform(get("/api/orgs/{id}/posts", orgId).param("channel", "law"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$[0].id").value("p2"));
    }

    @Test
    void 자료_조회는_문정원_채널로_라우팅한다() throws Exception {
        when(mattermost.collectChannelFiles(eq("chan-mj"))).thenReturn(List.of(
                new FileView("f1", "보고서.pdf", 1234L, "application/pdf", 100L, "송민지")));

        mvc.perform(get("/api/orgs/{id}/files", orgId).param("channel", "mj"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$[0].id").value("f1"))
                .andExpect(jsonPath("$[0].name").value("보고서.pdf"))
                .andExpect(jsonPath("$[0].uploader").value("송민지"));
    }

    @Test
    void 채널이_아직_생성되지_않았으면_409다() throws Exception {
        jdbc.update("delete from organization");
        Organization org = new Organization();
        org.setOrgNo("002");
        org.setOrgName("생성전기관");
        org.setChannelSlug("002");
        mapper.upsertBySeed(org);
        Long freshOrgId = mapper.findAll().get(0).getId();

        mvc.perform(get("/api/orgs/{id}/posts", freshOrgId).param("channel", "mj"))
                .andExpect(status().isConflict())
                .andExpect(jsonPath("$.message").value("해당 채널이 아직 생성되지 않았습니다."));
    }

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

        mvc.perform(get("/api/orgs/{id}/posts", missingId).param("channel", "mj"))
                .andExpect(status().isNotFound());
    }

    @Test
    void 알수없는_채널_구분이면_409다() throws Exception {
        mvc.perform(get("/api/orgs/{id}/posts", orgId).param("channel", "etc"))
                .andExpect(status().isConflict())
                .andExpect(jsonPath("$.message").value("알 수 없는 채널 구분입니다: etc"));
    }

    @Test
    void 파일_다운로드는_한글파일명을_UTF8로_인코딩한_Content_Disposition을_설정한다() throws Exception {
        when(mattermost.fileInfo(eq("f1")))
                .thenReturn(new FileRef("f1", "보고서.pdf", 1234L, "application/pdf"));
        when(mattermost.downloadFile(eq("f1"))).thenReturn("hello".getBytes());

        mvc.perform(get("/api/files/{fileId}", "f1"))
                .andExpect(status().isOk())
                .andExpect(header().string(HttpHeaders.CONTENT_DISPOSITION,
                        "attachment; filename*=UTF-8''%EB%B3%B4%EA%B3%A0%EC%84%9C.pdf"));
    }
}
