package kr.itn.itnhub.dashboard;

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.jdbc.core.JdbcTemplate;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.web.servlet.MockMvc;

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 DashboardControllerTest extends AbstractDbTest {

    @Autowired
    MockMvc mvc;

    @Autowired
    OrganizationMapper orgMapper;

    @Autowired
    JdbcTemplate jdbc;

    @MockBean
    MattermostClient mattermost;

    private Long noChannelId;
    private Long stage2Id;
    private Long stage11Id;

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

        // 001: 채널도 단계도 없는 기관 - 칸반의 "단계 미지정" 컬럼 대상
        noChannelId = seed("001", "채널없는기관");

        // 002: 채널 + 2단계, 권리확인 3건(완료 1건) / 권리처리 2건(완료 1건)
        stage2Id = seed("002", "권리확인중기관");
        orgMapper.updateChannelIdMj(stage2Id, "ch-mj");
        setStage(stage2Id, 2);
        insertReview(stage2Id, 1, "이용가능");
        insertReview(stage2Id, 2, null);
        insertReview(stage2Id, 3, "");
        insertProcess(stage2Id, 1, "처리완료");
        insertProcess(stage2Id, 2, "미처리");

        // 003: 채널 + 11단계 - 완료 집계 대상
        stage11Id = seed("003", "최종완료기관");
        orgMapper.updateChannelIdMj(stage11Id, "ch-mj-2");
        orgMapper.updateChannelIdLaw(stage11Id, "ch-law-2");
        setStage(stage11Id, 11);
    }

    private Long seed(String orgNo, String orgName) {
        Organization org = new Organization();
        org.setOrgNo(orgNo);
        org.setOrgName(orgName);
        org.setChannelSlug(orgNo);
        orgMapper.upsertBySeed(org);
        return orgMapper.findByOrgNoAndOrgName(orgNo, orgName).getId();
    }

    private void setStage(Long orgId, int stage) {
        orgMapper.updateStage(orgId, stage);
        jdbc.update("insert into stage_history (org_id, stage) values (?, ?)", orgId, stage);
    }

    private void insertReview(Long orgId, int seq, String reviewResult) {
        jdbc.update("insert into review_item (org_id, seq, review_result) values (?, ?, ?)",
                orgId, seq, reviewResult);
    }

    private void insertProcess(Long orgId, int seq, String status) {
        jdbc.update("insert into process_item (org_id, seq, process_status) values (?, ?, ?)",
                orgId, seq, status);
    }

    /**
     * KPI 기준 단계는 코드표(STAGE의 kpiFrom)에서 온다. 요구사항 [2]로 완료 기준이
     * ⑪법률검토 최종완료에서 ⑬보고서 제출 완료로 올라갔다 - 보고서가 실제로 올라온
     * 기관만 완료로 센다. 그래서 11단계인 003은 더 이상 완료가 아니다.
     */
    @Test
    void KPI는_전체_기관과_단계별_도달_기관수를_센다() throws Exception {
        mvc.perform(get("/api/dashboard"))
                .andExpect(status().isOk())
                // 신청 = 명부 전체 기관 수
                .andExpect(jsonPath("$.summary.applied").value(3))
                // 서류제출 = 2단계 이상 (002, 003)
                .andExpect(jsonPath("$.summary.docSubmitted").value(2))
                // 완료 = 13단계 이상 (없음)
                .andExpect(jsonPath("$.summary.completed").value(0))
                // 보고서 작성 = 12단계 이상 (없음)
                .andExpect(jsonPath("$.summary.reportWriting").value(0));
    }

    @Test
    void 완료는_보고서_제출까지_끝난_기관만_센다() throws Exception {
        setStage(stage11Id, 13);

        mvc.perform(get("/api/dashboard"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.summary.completed").value(1))
                .andExpect(jsonPath("$.summary.reportWriting").value(1));
    }

    @Test
    void KPI의_권리확인_권리처리는_전_기관_건수_합계다() throws Exception {
        mvc.perform(get("/api/dashboard"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.summary.reviewTotal").value(3))
                // 빈 문자열은 미완료로 센다
                .andExpect(jsonPath("$.summary.reviewDone").value(1))
                .andExpect(jsonPath("$.summary.processTotal").value(2))
                .andExpect(jsonPath("$.summary.processDone").value(1));
    }

    @Test
    void 권리관리_그래프는_DB의_분류값을_집계한다() throws Exception {
        jdbc.update("update review_item set kogl_type = '1유형', judged_kogl_type = '2유형', review_result = '신유형 개방', needs_processing = 'Y' where org_id = ? and seq = 1", stage2Id);
        jdbc.update("update review_item set review_result = '권리처리 추진 미희망' where org_id = ? and seq = 2", stage2Id);
        jdbc.update("update process_item set judged_kogl_type = '1유형', prior_kogl_type = '0유형' where org_id = ? and seq = 1", stage2Id);

        mvc.perform(get("/api/dashboard"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.rights.reviewSourceKoglTypes[0].label").value("미부착"))
                .andExpect(jsonPath("$.rights.reviewJudgedKoglTypes[0].label").value("2유형"))
                .andExpect(jsonPath("$.rights.processTypes.length()").value(2))
                .andExpect(jsonPath("$.rights.processStatuses.length()").value(2))
                .andExpect(jsonPath("$.rights.changedKoglCount").value(1))
                .andExpect(jsonPath("$.rights.reviewFreeUseCount").value(1))
                .andExpect(jsonPath("$.rights.reviewNeedsProcessingCount").value(1))
                .andExpect(jsonPath("$.rights.reviewNoProcessingCount").value(1));
    }

    @Test
    void RE는_데이터_모델이_없어_항상_0이다() throws Exception {
        mvc.perform(get("/api/dashboard"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.summary.unresolvedRe").value(0))
                .andExpect(jsonPath("$.orgs[0].reCount").value(0));
    }

    @Test
    void 기관행은_연번순이고_채널_보유_여부를_내려준다() throws Exception {
        mvc.perform(get("/api/dashboard"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.orgs.length()").value(3))
                .andExpect(jsonPath("$.orgs[0].orgNo").value("001"))
                .andExpect(jsonPath("$.orgs[0].status").value("INFO_PENDING"))
                .andExpect(jsonPath("$.orgs[0].hasChannel").value(false))
                .andExpect(jsonPath("$.orgs[0].stage").doesNotExist())
                .andExpect(jsonPath("$.orgs[1].orgNo").value("002"))
                .andExpect(jsonPath("$.orgs[1].hasChannel").value(true))
                .andExpect(jsonPath("$.orgs[1].status").value("PARTIAL"))
                .andExpect(jsonPath("$.orgs[1].stage").value(2));
    }

    @Test
    void 기관행에_권리확인_권리처리_진척이_기관별로_담긴다() throws Exception {
        mvc.perform(get("/api/dashboard"))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.orgs[0].reviewTotal").value(0))
                .andExpect(jsonPath("$.orgs[1].reviewTotal").value(3))
                .andExpect(jsonPath("$.orgs[1].reviewDone").value(1))
                .andExpect(jsonPath("$.orgs[1].processTotal").value(2))
                .andExpect(jsonPath("$.orgs[1].processDone").value(1));
    }

    @Test
    void 현재_단계_진입시각과_최근변동시각을_내려준다() throws Exception {
        mvc.perform(get("/api/dashboard"))
                .andExpect(status().isOk())
                // 단계 이력이 없는 기관은 진입시각이 없다
                .andExpect(jsonPath("$.orgs[0].stageChangedAt").doesNotExist())
                .andExpect(jsonPath("$.orgs[1].stageChangedAt").isNumber())
                // 최근변동은 organization.updated_at이 있어 항상 값이 있다
                .andExpect(jsonPath("$.orgs[0].lastChangedAt").isNumber())
                .andExpect(jsonPath("$.orgs[1].lastChangedAt").isNumber());
    }
}
