package incheon.product.geoview2d.download.service.impl;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;

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

/**
 * FileFormatConverterImpl 단위 테스트.
 * sanitizeFileName, CSV/GeoJSON 생성을 검증한다.
 */
class FileFormatConverterImplTest {

    private final FileFormatConverterImpl converter = new FileFormatConverterImpl();

    @Nested
    @DisplayName("sanitizeFileName — 파일명 정리")
    class SanitizeFileName {

        @Test
        @DisplayName("특수 문자(\\/:*?\"<>|) → 언더스코어로 치환")
        void specialCharsReplaced() {
            assertThat(converter.sanitizeFileName("file:name/test*data"))
                    .isEqualTo("file_name_test_data");
        }

        @Test
        @DisplayName("공백 → 언더스코어로 치환")
        void spacesReplaced() {
            assertThat(converter.sanitizeFileName("my file name"))
                    .isEqualTo("my_file_name");
        }

        @Test
        @DisplayName("연속 특수 문자 → 단일 언더스코어")
        void consecutiveSpecialCharsCollapsed() {
            assertThat(converter.sanitizeFileName("a:::b"))
                    .isEqualTo("a_b");
        }

        @Test
        @DisplayName("null → 'download' 기본값")
        void nullReturnsDefault() {
            assertThat(converter.sanitizeFileName(null))
                    .isEqualTo("download");
        }

        @Test
        @DisplayName("정상 파일명은 그대로 유지")
        void normalNameUnchanged() {
            assertThat(converter.sanitizeFileName("layer_data_2024"))
                    .isEqualTo("layer_data_2024");
        }
    }

    @Nested
    @DisplayName("CSV 파일 생성")
    class CsvCreation {

        @Test
        @DisplayName("BOM + 헤더 + 데이터 행 생성")
        void createsCsvWithBomAndHeader(@TempDir Path tempDir) throws IOException {
            Path csv = converter.createCsvFile(tempDir, "test", List.of("name", "code"),
                    consumer -> {
                        consumer.accept(Map.of("name", "서울", "code", "11"));
                        consumer.accept(Map.of("name", "인천", "code", "28"));
                    });

            String content = Files.readString(csv, StandardCharsets.UTF_8);
            assertThat(content).startsWith("\uFEFF");  // BOM
            assertThat(content).contains("name,code");  // 헤더
            assertThat(content).contains("서울,11");
            assertThat(content).contains("인천,28");
        }

        @Test
        @DisplayName("쉼표 포함 값은 따옴표로 감싸기")
        void csvEscapesCommas(@TempDir Path tempDir) throws IOException {
            Path csv = converter.createCsvFile(tempDir, "test", List.of("name"),
                    consumer -> consumer.accept(Map.of("name", "서울시, 강남구")));

            String content = Files.readString(csv, StandardCharsets.UTF_8);
            assertThat(content).contains("\"서울시, 강남구\"");
        }
    }

    @Nested
    @DisplayName("GeoJSON 파일 생성")
    class GeoJsonCreation {

        @Test
        @DisplayName("FeatureCollection 래퍼 구조")
        void createsValidGeoJsonStructure(@TempDir Path tempDir) throws IOException {
            Path json = converter.createGeoJsonFile(tempDir, "test",
                    consumer -> {
                        consumer.accept("{\"type\":\"Feature\",\"properties\":{\"id\":1}}");
                        consumer.accept("{\"type\":\"Feature\",\"properties\":{\"id\":2}}");
                    });

            String content = Files.readString(json, StandardCharsets.UTF_8);
            assertThat(content).startsWith("{\"type\":\"FeatureCollection\",\"features\":[");
            assertThat(content).endsWith("]}");
            assertThat(content).contains(",{\"type\":\"Feature\"");
        }
    }
}
