package kr.itn.itnhub.memo;

import kr.itn.itnhub.AbstractDbTest;
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.http.MediaType;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.test.context.support.WithAnonymousUser;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.web.servlet.MockMvc;

import static org.hamcrest.Matchers.containsString;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
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 WorkMemoControllerTest extends AbstractDbTest {

    @Autowired
    MockMvc mvc;

    @Autowired
    OrganizationMapper orgMapper;

    @Autowired
    WorkMemoMapper memoMapper;

    @Autowired
    JdbcTemplate jdbc;

    private Long orgId;

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

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

        orgId = orgMapper.findAll().get(0).getId();
    }

    @Test
    void 메모를_등록하면_생성된_메모를_돌려주고_목록에도_보인다() throws Exception {
        mvc.perform(post("/api/orgs/{id}/memos", orgId)
                        .with(csrf())
                        .contentType(MediaType.APPLICATION_JSON)
                        .content("""
                                {
                                  "contactName": "송민지 과장",
                                  "body": "채널 생성 일정 문의"
                                }
                                """))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.contactName").value("송민지 과장"))
                .andExpect(jsonPath("$.body").value("채널 생성 일정 문의"))
                .andExpect(jsonPath("$.createdAt").isNumber());

        mvc.perform(get("/api/orgs/{id}/memos", orgId))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$[0].contactName").value("송민지 과장"))
                .andExpect(jsonPath("$[0].body").value("채널 생성 일정 문의"));
    }

    @Test
    void 목록은_최신순이다() throws Exception {
        mvc.perform(post("/api/orgs/{id}/memos", orgId)
                        .with(csrf())
                        .contentType(MediaType.APPLICATION_JSON)
                        .content("""
                                {"contactName": "송민지", "body": "첫번째 통화"}
                                """))
                .andExpect(status().isOk());

        mvc.perform(post("/api/orgs/{id}/memos", orgId)
                        .with(csrf())
                        .contentType(MediaType.APPLICATION_JSON)
                        .content("""
                                {"contactName": "송민지", "body": "두번째 통화"}
                                """))
                .andExpect(status().isOk());

        mvc.perform(get("/api/orgs/{id}/memos", orgId))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$[0].body").value("두번째 통화"))
                .andExpect(jsonPath("$[1].body").value("첫번째 통화"));
    }

    @Test
    void 메모_내용이_비면_400과_한글_메시지를_돌려준다() throws Exception {
        mvc.perform(post("/api/orgs/{id}/memos", orgId)
                        .with(csrf())
                        .contentType(MediaType.APPLICATION_JSON)
                        .content("""
                                {"contactName": "송민지", "body": ""}
                                """))
                .andExpect(status().isBadRequest())
                .andExpect(jsonPath("$.message").value(containsString("메모 내용")));
    }

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

        mvc.perform(get("/api/orgs/{id}/memos", missingId))
                .andExpect(status().isNotFound())
                .andExpect(jsonPath("$.message").value(containsString(String.valueOf(missingId))));
    }

    @Test
    void 메모를_삭제하면_204와_함께_목록에서_사라진다() throws Exception {
        mvc.perform(post("/api/orgs/{id}/memos", orgId)
                        .with(csrf())
                        .contentType(MediaType.APPLICATION_JSON)
                        .content("""
                                {"contactName": "송민지", "body": "삭제될 메모"}
                                """))
                .andExpect(status().isOk());
        Long memoId = memoMapper.findByOrg(orgId).get(0).getId();

        mvc.perform(delete("/api/orgs/{id}/memos/{memoId}", orgId, memoId).with(csrf()))
                .andExpect(status().isNoContent());

        mvc.perform(get("/api/orgs/{id}/memos", orgId))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$").isEmpty());
    }

    @Test
    void 다른_기관_경로로_삭제하면_404다() throws Exception {
        mvc.perform(post("/api/orgs/{id}/memos", orgId)
                        .with(csrf())
                        .contentType(MediaType.APPLICATION_JSON)
                        .content("""
                                {"contactName": "송민지", "body": "삭제될 메모"}
                                """))
                .andExpect(status().isOk());
        Long memoId = memoMapper.findByOrg(orgId).get(0).getId();

        Organization other = new Organization();
        other.setOrgNo("002");
        other.setOrgName("다른기관");
        other.setChannelSlug("002");
        orgMapper.upsertBySeed(other);
        Long otherOrgId = orgMapper.findAll().stream()
                .filter(o -> o.getOrgNo().equals("002"))
                .findFirst().get().getId();

        mvc.perform(delete("/api/orgs/{id}/memos/{memoId}", otherOrgId, memoId).with(csrf()))
                .andExpect(status().isNotFound());
    }

    @Test
    @WithAnonymousUser
    void 인증없이_호출하면_401이다() throws Exception {
        mvc.perform(get("/api/orgs/{id}/memos", orgId))
                .andExpect(status().isUnauthorized());
    }
}
