package gc.mda.signal_batch.util;

import gc.mda.signal_batch.global.util.SignalKindCode;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.NullAndEmptySource;
import org.junit.jupiter.params.provider.ValueSource;

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

@DisplayName("SignalKindCode - MDA 선종 범례코드 치환")
class SignalKindCodeTest {

    @Nested
    @DisplayName("vesselType 단독 매칭")
    class VesselTypeDirect {

        @ParameterizedTest(name = "vesselType={0} → {1}")
        @CsvSource({
            "cargo, CARGO",
            "tanker, TANKER",
            "passenger, FERRY",
            "aton, BUOY",
            "law enforcement, GOV",
            "search and rescue, KCGV",
            "local vessel, FISHING"
        })
        @DisplayName("직접 매칭 케이스")
        void resolve_directMatch(String vesselType, String expectedName) {
            SignalKindCode result = SignalKindCode.resolve(vesselType, null);
            assertThat(result.name()).isEqualTo(expectedName);
        }
    }

    @Nested
    @DisplayName("vesselType 그룹 매칭")
    class VesselTypeGroup {

        @ParameterizedTest(name = "vesselType={0} → GOV")
        @ValueSource(strings = {"tug", "pilot boat", "tender", "anti pollution", "medical transport"})
        @DisplayName("GOV 그룹 매칭")
        void resolve_govGroup(String vesselType) {
            assertThat(SignalKindCode.resolve(vesselType, null)).isEqualTo(SignalKindCode.GOV);
        }

        @ParameterizedTest(name = "vesselType={0} → FERRY")
        @ValueSource(strings = {"high speed craft", "wing in ground-effect"})
        @DisplayName("FERRY 그룹 매칭")
        void resolve_ferryGroup(String vesselType) {
            assertThat(SignalKindCode.resolve(vesselType, null)).isEqualTo(SignalKindCode.FERRY);
        }
    }

    @Nested
    @DisplayName("Vessel + extraInfo 조합")
    class VesselExtraInfo {

        @Test
        @DisplayName("Vessel + fishing → FISHING")
        void resolve_vesselFishing() {
            assertThat(SignalKindCode.resolve("Vessel", "Fishing")).isEqualTo(SignalKindCode.FISHING);
        }

        @Test
        @DisplayName("Vessel + military operations → GOV")
        void resolve_vesselMilitary() {
            assertThat(SignalKindCode.resolve("Vessel", "Military Operations")).isEqualTo(SignalKindCode.GOV);
        }

        @ParameterizedTest(name = "Vessel + {0} → GOV")
        @ValueSource(strings = {"towing", "towing (large)", "dredging/underwater ops", "diving operations"})
        @DisplayName("Vessel + 해양작업 → GOV")
        void resolve_vesselMarineOps(String extraInfo) {
            assertThat(SignalKindCode.resolve("Vessel", extraInfo)).isEqualTo(SignalKindCode.GOV);
        }

        @ParameterizedTest(name = "Vessel + {0} → FISHING")
        @ValueSource(strings = {"pleasure craft", "sailing", "n/a"})
        @DisplayName("Vessel + 레저/기타 → FISHING")
        void resolve_vesselLeisure(String extraInfo) {
            assertThat(SignalKindCode.resolve("Vessel", extraInfo)).isEqualTo(SignalKindCode.FISHING);
        }

        @Test
        @DisplayName("Vessel + hazardous cat X → CARGO")
        void resolve_vesselHazardous() {
            assertThat(SignalKindCode.resolve("Vessel", "Hazardous Cat A")).isEqualTo(SignalKindCode.CARGO);
        }

        @Test
        @DisplayName("Vessel + 미매칭 extraInfo → DEFAULT")
        void resolve_vesselUnknownExtra() {
            assertThat(SignalKindCode.resolve("Vessel", "some-unknown")).isEqualTo(SignalKindCode.DEFAULT);
        }
    }

    @Nested
    @DisplayName("N/A + extraInfo 조합")
    class NaExtraInfo {

        @Test
        @DisplayName("N/A + hazardous cat → CARGO")
        void resolve_naHazardous() {
            assertThat(SignalKindCode.resolve("N/A", "Hazardous Cat B")).isEqualTo(SignalKindCode.CARGO);
        }

        @Test
        @DisplayName("N/A + other → DEFAULT")
        void resolve_naOther() {
            assertThat(SignalKindCode.resolve("N/A", "Other")).isEqualTo(SignalKindCode.DEFAULT);
        }
    }

    @Nested
    @DisplayName("Null/Blank/대소문자 처리")
    class EdgeCases {

        @ParameterizedTest
        @NullAndEmptySource
        @ValueSource(strings = {"  ", "\t"})
        @DisplayName("vesselType이 null/blank → DEFAULT")
        void resolve_nullOrBlankVesselType(String vesselType) {
            assertThat(SignalKindCode.resolve(vesselType, null)).isEqualTo(SignalKindCode.DEFAULT);
        }

        @Test
        @DisplayName("대소문자 무시 (case-insensitive)")
        void resolve_caseInsensitive() {
            assertThat(SignalKindCode.resolve("CARGO", null)).isEqualTo(SignalKindCode.CARGO);
            assertThat(SignalKindCode.resolve("Tanker", null)).isEqualTo(SignalKindCode.TANKER);
            assertThat(SignalKindCode.resolve("PASSENGER", null)).isEqualTo(SignalKindCode.FERRY);
        }

        @Test
        @DisplayName("알 수 없는 vesselType → DEFAULT (fallback)")
        void resolve_unknownType() {
            assertThat(SignalKindCode.resolve("unknown-xyz", null)).isEqualTo(SignalKindCode.DEFAULT);
        }

        @Test
        @DisplayName("양쪽 모두 null → DEFAULT")
        void resolve_bothNull() {
            assertThat(SignalKindCode.resolve(null, null)).isEqualTo(SignalKindCode.DEFAULT);
        }
    }

    @Nested
    @DisplayName("코드 값 검증")
    class CodeValues {

        @Test
        @DisplayName("각 enum의 code 값이 올바른지 확인")
        void codeValues() {
            assertThat(SignalKindCode.FISHING.getCode()).isEqualTo("000020");
            assertThat(SignalKindCode.KCGV.getCode()).isEqualTo("000021");
            assertThat(SignalKindCode.FERRY.getCode()).isEqualTo("000022");
            assertThat(SignalKindCode.CARGO.getCode()).isEqualTo("000023");
            assertThat(SignalKindCode.TANKER.getCode()).isEqualTo("000024");
            assertThat(SignalKindCode.GOV.getCode()).isEqualTo("000025");
            assertThat(SignalKindCode.DEFAULT.getCode()).isEqualTo("000027");
            assertThat(SignalKindCode.BUOY.getCode()).isEqualTo("000028");
        }
    }
}
