package kr.itn.itnhub.contact;

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.WithMockUser;
import org.springframework.test.web.servlet.MockMvc;

import static org.assertj.core.api.Assertions.assertThat;
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.request.MockMvcRequestBuilders.put;
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 ContactControllerTest extends AbstractDbTest {

    @Autowired
    MockMvc mvc;

    @Autowired
    ContactMapper contactMapper;

    @Autowired
    OrganizationMapper orgMapper;

    @Autowired
    JdbcTemplate jdbc;

    @BeforeEach
    void clean() {
        jdbc.update("delete from organization");
        jdbc.update("delete from contact");
    }

    @Test
    void 담당자를_등록하면_저장된_값을_돌려준다() throws Exception {
        mvc.perform(post("/api/contacts")
                        .with(csrf())
                        .contentType(MediaType.APPLICATION_JSON)
                        .content("""
                                {
                                  "category": "APPLICANT",
                                  "name": "송민지",
                                  "affiliation": "국제방송교류재단",
                                  "deptName": "데이터정보화팀",
                                  "title": "과장",
                                  "phone": "02-3475-5434",
                                  "email": "ming@arirang.com"
                                }
                                """))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.category").value("APPLICANT"))
                .andExpect(jsonPath("$.name").value("송민지"))
                .andExpect(jsonPath("$.affiliation").value("국제방송교류재단"))
                .andExpect(jsonPath("$.id").isNumber());

        assertThat(contactMapper.findAll(null)).hasSize(1);
    }

    @Test
    void 구분_필터로_목록을_조회한다() throws Exception {
        Contact applicant = new Contact();
        applicant.setCategory("APPLICANT");
        applicant.setName("송민지");
        contactMapper.insert(applicant);

        Contact lawyer = new Contact();
        lawyer.setCategory("LAWYER");
        lawyer.setName("이변호");
        contactMapper.insert(lawyer);

        mvc.perform(get("/api/contacts").param("category", "LAWYER"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.length()").value(1))
                .andExpect(jsonPath("$[0].name").value("이변호"));

        mvc.perform(get("/api/contacts"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.length()").value(2));
    }

    @Test
    void 구분값이_잘못되면_등록시_400이다() throws Exception {
        mvc.perform(post("/api/contacts")
                        .with(csrf())
                        .contentType(MediaType.APPLICATION_JSON)
                        .content("""
                                { "category": "UNKNOWN", "name": "홍길동" }
                                """))
                .andExpect(status().isBadRequest());
    }

    @Test
    void 성명이_비면_등록시_400이다() throws Exception {
        mvc.perform(post("/api/contacts")
                        .with(csrf())
                        .contentType(MediaType.APPLICATION_JSON)
                        .content("""
                                { "category": "APPLICANT", "name": "" }
                                """))
                .andExpect(status().isBadRequest())
                .andExpect(jsonPath("$.message").value(org.hamcrest.Matchers.containsString("성명")));
    }

    @Test
    void 담당자를_수정하면_변경된_값이_저장된다() throws Exception {
        Contact contact = new Contact();
        contact.setCategory("MJ");
        contact.setName("김문정");
        contactMapper.insert(contact);

        mvc.perform(put("/api/contacts/{id}", contact.getId())
                        .with(csrf())
                        .contentType(MediaType.APPLICATION_JSON)
                        .content("""
                                {
                                  "category": "MJ",
                                  "name": "김문정",
                                  "phone": "02-1234-5678",
                                  "email": "mj@mcst.go.kr"
                                }
                                """))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.phone").value("02-1234-5678"))
                .andExpect(jsonPath("$.email").value("mj@mcst.go.kr"));
    }

    @Test
    void 존재하지_않는_담당자를_수정하면_404다() throws Exception {
        mvc.perform(put("/api/contacts/{id}", 999999)
                        .with(csrf())
                        .contentType(MediaType.APPLICATION_JSON)
                        .content("""
                                { "category": "MJ", "name": "김문정" }
                                """))
                .andExpect(status().isNotFound());
    }

    @Test
    void 존재하지_않는_담당자를_삭제하면_404다() throws Exception {
        mvc.perform(delete("/api/contacts/{id}", 999999).with(csrf()))
                .andExpect(status().isNotFound());
    }

    @Test
    void 담당자를_삭제하면_배정되어_있던_기관의_배정도_함께_해제된다() throws Exception {
        Contact contact = new Contact();
        contact.setCategory("APPLICANT");
        contact.setName("송민지");
        contactMapper.insert(contact);

        Organization org = new Organization();
        org.setOrgNo("001");
        org.setOrgName("국제방송교류재단");
        org.setChannelSlug("001");
        orgMapper.upsertBySeed(org);
        Long orgId = orgMapper.findAll().get(0).getId();
        orgMapper.updateAssignments(orgId, contact.getId(), null, null, null);

        assertThat(orgMapper.findById(orgId).getStatus().name()).isEqualTo("READY");

        mvc.perform(delete("/api/contacts/{id}", contact.getId()).with(csrf()))
                .andExpect(status().isNoContent());

        assertThat(contactMapper.findById(contact.getId())).isNull();

        Organization after = orgMapper.findById(orgId);
        assertThat(after.getApplicantContactId()).isNull();
        assertThat(after.getStatus().name()).isEqualTo("INFO_PENDING");
    }
}
