package gc.mda.signal_batch.batch.processor;

import gc.mda.signal_batch.domain.vessel.dto.AisTargetDto;
import gc.mda.signal_batch.domain.vessel.model.AisTargetEntity;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

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

class AisTargetDataProcessorTest {

    private AisTargetDataProcessor processor;

    @BeforeEach
    void setUp() {
        processor = new AisTargetDataProcessor();
    }

    private AisTargetDto createValidDto() {
        return AisTargetDto.builder()
                .mmsi("440331240")
                .lat(34.227527)
                .lon(127.063800)
                .sog(10.4)
                .cog(125.0)
                .heading(130.0)
                .name("TEST VESSEL")
                .callsign("DSAB")
                .vesselType("Cargo")
                .extraInfo(null)
                .imo(9123456L)
                .length(120)
                .width(20)
                .draught(6.5)
                .destination("BUSAN")
                .status("Under Way Using Engine")
                .rot(0)
                .messageTimestamp("2025-08-26T10:30:45Z")
                .eta("2025-09-01T14:00:00Z")
                .build();
    }

    @Nested
    @DisplayName("유효성 검사")
    class Validation {

        @Test
        @DisplayName("유효한 데이터 → Entity 변환 성공")
        void process_validDto_returnsEntity() {
            AisTargetDto dto = createValidDto();

            AisTargetEntity entity = processor.process(dto);

            assertThat(entity).isNotNull();
            assertThat(entity.getMmsi()).isEqualTo("440331240");
            assertThat(entity.getLat()).isEqualTo(34.227527);
            assertThat(entity.getLon()).isEqualTo(127.063800);
            assertThat(entity.getName()).isEqualTo("TEST VESSEL");
            assertThat(entity.getImo()).isEqualTo(9123456L);
            assertThat(entity.getVesselType()).isEqualTo("Cargo");
            assertThat(entity.getSog()).isEqualTo(10.4);
            assertThat(entity.getMessageTimestamp()).isNotNull();
        }

        @Test
        @DisplayName("MMSI null → null 반환")
        void process_nullMmsi_returnsNull() {
            AisTargetDto dto = createValidDto();
            dto.setMmsi(null);

            assertThat(processor.process(dto)).isNull();
        }

        @Test
        @DisplayName("MMSI 빈 문자열 → null 반환")
        void process_blankMmsi_returnsNull() {
            AisTargetDto dto = createValidDto();
            dto.setMmsi("  ");

            assertThat(processor.process(dto)).isNull();
        }

        @Test
        @DisplayName("Lat null → null 반환")
        void process_nullLat_returnsNull() {
            AisTargetDto dto = createValidDto();
            dto.setLat(null);

            assertThat(processor.process(dto)).isNull();
        }

        @Test
        @DisplayName("Lon null → null 반환")
        void process_nullLon_returnsNull() {
            AisTargetDto dto = createValidDto();
            dto.setLon(null);

            assertThat(processor.process(dto)).isNull();
        }
    }

    @Nested
    @DisplayName("타임스탬프 파싱")
    class TimestampParsing {

        @Test
        @DisplayName("유효한 ISO 8601 타임스탬프 파싱")
        void process_validTimestamp_parsed() {
            AisTargetDto dto = createValidDto();
            dto.setMessageTimestamp("2025-08-26T10:30:45Z");

            AisTargetEntity entity = processor.process(dto);

            assertThat(entity).isNotNull();
            assertThat(entity.getMessageTimestamp()).isNotNull();
            assertThat(entity.getMessageTimestamp().getYear()).isEqualTo(2025);
            assertThat(entity.getMessageTimestamp().getMonthValue()).isEqualTo(8);
            assertThat(entity.getMessageTimestamp().getDayOfMonth()).isEqualTo(26);
        }

        @Test
        @DisplayName("잘못된 타임스탬프 → null 반환")
        void process_invalidTimestamp_returnsNull() {
            AisTargetDto dto = createValidDto();
            dto.setMessageTimestamp("invalid-date");

            assertThat(processor.process(dto)).isNull();
        }

        @Test
        @DisplayName("null 타임스탬프 → null 반환")
        void process_nullTimestamp_returnsNull() {
            AisTargetDto dto = createValidDto();
            dto.setMessageTimestamp(null);

            assertThat(processor.process(dto)).isNull();
        }

        @Test
        @DisplayName("빈 문자열 타임스탬프 → null 반환")
        void process_emptyTimestamp_returnsNull() {
            AisTargetDto dto = createValidDto();
            dto.setMessageTimestamp("");

            assertThat(processor.process(dto)).isNull();
        }
    }

    @Nested
    @DisplayName("ETA 파싱")
    class EtaParsing {

        @Test
        @DisplayName("유효한 ETA → 파싱 성공")
        void process_validEta_parsed() {
            AisTargetDto dto = createValidDto();

            AisTargetEntity entity = processor.process(dto);

            assertThat(entity).isNotNull();
            assertThat(entity.getEta()).isNotNull();
            assertThat(entity.getEta().getYear()).isEqualTo(2025);
        }

        @Test
        @DisplayName("특수값 ETA (9999-12-31) → null 처리")
        void process_specialEta_null() {
            AisTargetDto dto = createValidDto();
            dto.setEta("9999-12-31T23:59:59Z");

            AisTargetEntity entity = processor.process(dto);

            assertThat(entity).isNotNull();
            assertThat(entity.getEta()).isNull();
        }

        @Test
        @DisplayName("null ETA → null 처리")
        void process_nullEta_null() {
            AisTargetDto dto = createValidDto();
            dto.setEta(null);

            AisTargetEntity entity = processor.process(dto);

            assertThat(entity).isNotNull();
            assertThat(entity.getEta()).isNull();
        }
    }

    @Test
    @DisplayName("전체 필드 매핑 검증")
    void process_allFields_mappedCorrectly() {
        AisTargetDto dto = createValidDto();

        AisTargetEntity entity = processor.process(dto);

        assertThat(entity).isNotNull();
        assertThat(entity.getMmsi()).isEqualTo(dto.getMmsi());
        assertThat(entity.getImo()).isEqualTo(dto.getImo());
        assertThat(entity.getName()).isEqualTo(dto.getName());
        assertThat(entity.getCallsign()).isEqualTo(dto.getCallsign());
        assertThat(entity.getVesselType()).isEqualTo(dto.getVesselType());
        assertThat(entity.getExtraInfo()).isEqualTo(dto.getExtraInfo());
        assertThat(entity.getLat()).isEqualTo(dto.getLat());
        assertThat(entity.getLon()).isEqualTo(dto.getLon());
        assertThat(entity.getHeading()).isEqualTo(dto.getHeading());
        assertThat(entity.getSog()).isEqualTo(dto.getSog());
        assertThat(entity.getCog()).isEqualTo(dto.getCog());
        assertThat(entity.getRot()).isEqualTo(dto.getRot());
        assertThat(entity.getLength()).isEqualTo(dto.getLength());
        assertThat(entity.getWidth()).isEqualTo(dto.getWidth());
        assertThat(entity.getDraught()).isEqualTo(dto.getDraught());
        assertThat(entity.getDestination()).isEqualTo(dto.getDestination());
        assertThat(entity.getStatus()).isEqualTo(dto.getStatus());
    }

    @Test
    @DisplayName("문자 혼합 MMSI도 정상 처리")
    void process_mixedMmsi_handled() {
        AisTargetDto dto = createValidDto();
        dto.setMmsi("MID123456");

        AisTargetEntity entity = processor.process(dto);

        assertThat(entity).isNotNull();
        assertThat(entity.getMmsi()).isEqualTo("MID123456");
    }
}
