package kr.itn.itnhub.code;

import kr.itn.itnhub.AbstractDbTest;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;

import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;

class CodeServiceTest extends AbstractDbTest {

    @Autowired
    CodeService service;

    @Autowired
    JdbcTemplate jdbc;

    @AfterEach
    void restore() {
        jdbc.update("update code set active = true where group_id = 'CONTACT_METHOD'");
        service.reload();
    }

    @Test
    void 그룹을_정렬순서대로_돌려준다() {
        List<Code> stages = service.group(Codes.STAGE);

        assertThat(stages).hasSize(13);
        assertThat(stages.get(0).code()).isEqualTo("1");
        assertThat(stages.get(12).label()).isEqualTo("보고서 제출 완료");
    }

    @Test
    void attrs가_맵으로_파싱된다() {
        Code stage13 = service.find(Codes.STAGE, "13").orElseThrow();

        assertThat(stage13.attr("progress")).isEqualTo("done");
        assertThat(stage13.attr("kpiFrom")).isEqualTo("completed");
        assertThat(stage13.attr("groupKey")).isEqualTo("closing");
        assertThat(stage13.attr("없는속성")).isNull();
    }

    @Test
    void 불리언_속성을_읽는다() {
        assertThat(service.find(Codes.PROCESS_STATUS, "처리완료").orElseThrow().flag("done")).isTrue();
        assertThat(service.find(Codes.PROCESS_STATUS, "미처리").orElseThrow().flag("done")).isFalse();
        // 속성이 아예 없는 코드도 false여야 한다.
        assertThat(service.find(Codes.CONTRACT_DOC, "공문").orElseThrow().flag("freeText")).isFalse();
        assertThat(service.find(Codes.CONTRACT_DOC, "기타").orElseThrow().flag("freeText")).isTrue();
    }

    @Test
    void 사용처별로_공공누리_유형을_거른다() {
        assertThat(service.scoped(Codes.KOGL_TYPE, Codes.SCOPE_REVIEW)).hasSize(7);
        assertThat(service.scoped(Codes.KOGL_TYPE, Codes.SCOPE_PROCESS))
                .hasSize(6)
                .noneMatch(c -> c.code().equals("보류"));
        // prior에는 기관 조사표에서 온 값('유형 확인 필요', '제4유형')까지 들어간다.
        // 운영 DB에 실제로 저장돼 있는 값이라 등록해 두지 않으면 화면에서 선택 안 됨으로 보인다.
        assertThat(service.scoped(Codes.KOGL_TYPE, Codes.SCOPE_PRIOR))
                .extracting(Code::code)
                .containsExactly("0유형", "1유형", "2유형", "3유형", "4유형",
                        "유형 확인 필요", "제4유형");
    }

    @Test
    void scopes가_없는_그룹은_빈_결과다() {
        assertThat(service.scoped(Codes.CONTACT_METHOD, Codes.SCOPE_REVIEW)).isEmpty();
    }

    @Test
    void 비활성_코드는_목록에서_빠지지만_조회는_된다() {
        jdbc.update("update code set active = false "
                + "where group_id = 'CONTACT_METHOD' and code = '방문'");
        service.reload();

        assertThat(service.group(Codes.CONTACT_METHOD))
                .extracting(Code::code)
                .containsExactly("전화", "메일", "기타");
        // 이미 '방문'으로 저장된 과거 데이터를 화면에 그리려면 조회는 되어야 한다.
        assertThat(service.find(Codes.CONTACT_METHOD, "방문")).isPresent();
    }

    @Test
    void 유효성_검사는_활성_코드만_통과시킨다() {
        assertThat(service.isValid(Codes.PROCESS_STATUS, "처리완료")).isTrue();
        assertThat(service.isValid(Codes.PROCESS_STATUS, "없는값")).isFalse();

        jdbc.update("update code set active = false "
                + "where group_id = 'CONTACT_METHOD' and code = '방문'");
        service.reload();

        assertThat(service.isValid(Codes.CONTACT_METHOD, "방문")).isFalse();
    }

    @Test
    void 전체_조회는_활성_코드만_그룹별로_담는다() {
        assertThat(service.all())
                .containsKeys(Codes.STAGE, Codes.KOGL_TYPE, Codes.CONTACT_CATEGORY)
                .hasSize(12);
    }

    @Test
    void 없는_그룹은_빈_목록이고_없는_코드는_빈_결과다() {
        assertThat(service.group("NO_SUCH_GROUP")).isEmpty();
        assertThat(service.find("NO_SUCH_GROUP", "A")).isEmpty();
        assertThat(service.find(Codes.STAGE, "999")).isEmpty();
    }

    @Test
    void 그룹_메타를_돌려준다() {
        assertThat(service.groups())
                .hasSize(12)
                .anySatisfy(g -> {
                    assertThat(g.groupId()).isEqualTo("STAGE");
                    assertThat(g.groupName()).isEqualTo("진행단계");
                });
    }
}
