package incheon.ags.mrb.upload.web;

import incheon.ags.mrb.main.exception.ErrorCode;
import incheon.ags.mrb.main.exception.RecipeException;
import incheon.ags.mrb.upload.mapper.IncheonServiceMapper;
import incheon.ags.mrb.upload.service.ProcessFileService;
import incheon.ags.mrb.upload.vo.FileUploadRequestDTO;
import incheon.com.cmm.api.DefaultApiResponse;
import incheon.com.cmm.context.RequestContext;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/upload")
@RequiredArgsConstructor
public class FileUploadController {

    private static final Logger logger = LoggerFactory.getLogger(FileUploadController.class);

    private final ProcessFileService processFileService;
    private final IncheonServiceMapper mapper;

    // ========================================================================
    // 1. 파일 최종 업로드 (Upload)
    // ========================================================================
    @PostMapping("/upload.do")
    public ResponseEntity<DefaultApiResponse<Map<String, Object>>> handleFileUpload(
            @ModelAttribute FileUploadRequestDTO requestDTO) throws Exception {

        // [검증 1] 레이어 이름
        if (!StringUtils.hasText(requestDTO.getLayerName())) {
            throw new RecipeException(ErrorCode.INVALID_INPUT_VALUE, "레이어 이름을 입력해주세요.");
        }

        // [검증 2] 파일 존재 여부 및 파일명 보안 체크 (Path Traversal 방지)
        validateFile(requestDTO.getFile());

        // [검증 3] 중복 체크
        String currentUserId = RequestContext.getCurrentUserId();
        String userIdCheck = StringUtils.hasText(currentUserId) ? currentUserId : null;

        boolean exists = mapper.checkLayerNameExistsForUser(userIdCheck, requestDTO.getLayerName().trim());
        if (exists) {
            Map<String, Object> errorData = new HashMap<>();
            errorData.put("duplicateName", requestDTO.getLayerName().trim());

            return ResponseEntity.status(409).body(
                    DefaultApiResponse.<Map<String, Object>>builder()
                            .code(409)
                            .message("이미 존재하는 레이어 이름입니다.")
                            .data(errorData)
                            .build());
        }

        // [실행]
        logger.info("파일 처리 시작: {}", requestDTO.getFile().getOriginalFilename());
        Map<String, Object> resultData = processFileService.processFile(requestDTO);

        // [결과 확인]
        if ("error".equals(resultData.get("status"))) {
            if (Boolean.TRUE.equals(resultData.get("geometryMismatch"))) {
                // 400 Bad Request
                return ResponseEntity.badRequest().body(
                        DefaultApiResponse.<Map<String, Object>>builder()
                                .code(400)
                                .message((String) resultData.getOrDefault("message", "파일 처리 실패"))
                                .data(resultData)
                                .build());
            }
            throw new RecipeException(ErrorCode.FILE_PARSE_FAILED,
                    (String) resultData.getOrDefault("message", "파일 처리 실패"));
        }

        return ResponseEntity.ok(DefaultApiResponse.success(resultData, "파일이 성공적으로 업로드 및 처리되었습니다."));
    }

    // ========================================================================
    // 2. Shape 컬럼 추출 (Columns only)
    // ========================================================================
    @PostMapping("/shape/columns.do")
    public ResponseEntity<DefaultApiResponse<Map<String, Object>>> getShapeColumns(
            @RequestParam("shapefile") MultipartFile file,
            @RequestParam("previewCounterShape") int previewCounterShape,
            @RequestParam("encoding") String encoding) throws Exception {

        validateFile(file); // 빈 파일 검사

        Map<String, Object> resultShape = processFileService.getShapeColumns(file, previewCounterShape, encoding);

        if (Boolean.FALSE.equals(resultShape.get("success"))) {
            throw new RecipeException(ErrorCode.FILE_PARSE_FAILED, (String) resultShape.get("message"));
        }

        Map<String, Object> dataWrapper = new HashMap<>();
        dataWrapper.put("columns", resultShape.get("columns"));
        dataWrapper.put("rows", resultShape.get("rows"));

        return ResponseEntity.ok(DefaultApiResponse.success(dataWrapper, "Shapefile 컬럼 정보를 성공적으로 추출했습니다."));
    }

    // ========================================================================
    // 3. Shape 미리보기 (Preview with Coordinate)
    // ========================================================================
    @PostMapping("/shape.do")
    public ResponseEntity<DefaultApiResponse<Map<String, Object>>> handleShapeFile(
            @RequestParam("shapefile") MultipartFile file,
            @RequestParam("previewCounterShape") int previewCounterShape,
            // [중요] 프론트엔드 파라미터명 'Shapecoordinate' 유지
            @RequestParam("Shapecoordinate") String coordinate,
            @RequestParam("encoding") String encoding) throws Exception {

        validateFile(file);

        Map<String, Object> resultShape = processFileService.previewShapeFile(file, previewCounterShape, coordinate,
                encoding);

        if (Boolean.FALSE.equals(resultShape.get("success"))) {
            throw new RecipeException(ErrorCode.FILE_PARSE_FAILED, (String) resultShape.get("message"));
        }

        Map<String, Object> dataWrapper = new HashMap<>();
        dataWrapper.put("columns", resultShape.get("columns"));
        dataWrapper.put("rows", resultShape.get("rows"));
        dataWrapper.put("geojson", resultShape.get("geojson"));

        return ResponseEntity.ok(DefaultApiResponse.success(dataWrapper, "Shapefile 미리보기를 생성했습니다."));
    }

    // ========================================================================
    // 4. 통합 파일 미리보기 (CSV, Shape ZIP)
    // ========================================================================
    @PostMapping("/previewShape.do")
    public ResponseEntity<DefaultApiResponse<Map<String, Object>>> previewFile(
            // [중요] 프론트엔드 파라미터명 'currentPreviewFile' 유지
            @RequestParam("currentPreviewFile") MultipartFile currentPreviewFile,
            @RequestParam("coordinate") String coordinate,
            @RequestParam("encoding") String encoding,
            @RequestParam("limit") int limit,
            @RequestParam(value = "firstColumn", required = false) String firstColumn,
            @RequestParam(value = "secondColumn", required = false) String secondColumn,
            @RequestParam(value = "wktColumn", required = false) String wktColumn) throws Exception {

        validateFile(currentPreviewFile);

        String originalFileName = currentPreviewFile.getOriginalFilename();
        boolean isShapeFile = originalFileName != null && originalFileName.toLowerCase().endsWith(".zip");

        Map<String, Object> result;

        if (isShapeFile) {
            result = processFileService.previewShapeFile(currentPreviewFile, limit, coordinate, encoding);
        } else {
            // CSV 처리 검증
            if (StringUtils.hasText(wktColumn)) {
                result = processFileService.previewCsvWktFile(currentPreviewFile, limit, coordinate, wktColumn,
                        encoding);
            } else if (StringUtils.hasText(firstColumn) && StringUtils.hasText(secondColumn)) {
                result = processFileService.previewCsvFile(currentPreviewFile, limit, coordinate, firstColumn,
                        secondColumn, encoding);
            } else {
                throw new RecipeException(ErrorCode.INVALID_INPUT_VALUE, "CSV 처리를 위한 좌표 컬럼 정보가 부족합니다.");
            }
        }

        if (result == null || Boolean.FALSE.equals(result.get("success"))) {
            throw new RecipeException(ErrorCode.FILE_PARSE_FAILED,
                    (String) (result != null ? result.get("message") : "파일 변환 실패"));
        }

        return ResponseEntity.ok(DefaultApiResponse.success(result, "파일 컬럼 정보를 성공적으로 추출했습니다."));
    }

    // ========================================================================
    // 5. DXF 미리보기
    // ========================================================================
    @PostMapping("/dxf/preview")
    public ResponseEntity<DefaultApiResponse<Map<String, Object>>> previewDxfLayer(
            @RequestParam("file") MultipartFile file,
            @RequestParam("layerName") String layerName,
            @RequestParam(value = "coordinate", required = false) String coordinate,
            @RequestParam(value = "limit", defaultValue = "5") int limit) throws Exception {

        validateFile(file);
        if (!StringUtils.hasText(layerName)) {
            throw new RecipeException(ErrorCode.INVALID_INPUT_VALUE, "레이어 이름이 필요합니다.");
        }

        Map<String, Object> result = processFileService.previewDxfLayer(file, layerName, coordinate, limit);

        if (Boolean.FALSE.equals(result.get("success"))) {
            throw new RecipeException(ErrorCode.FILE_PARSE_FAILED,
                    (String) result.getOrDefault("message", "DXF 미리보기 실패"));
        }

        return ResponseEntity.ok(DefaultApiResponse.success(result));
    }

    // ========================================================================
    // 6. DXF 분석
    // ========================================================================
    @PostMapping("/dxf/analyze")
    public ResponseEntity<DefaultApiResponse<Map<String, Object>>> analyzeDxfFile(
            @RequestParam("file") MultipartFile file,
            @RequestParam(value = "coordinate", required = false) String coordinate,
            @RequestParam(value = "limit", defaultValue = "5") int limit) throws Exception {

        validateFile(file);

        Map<String, Object> result = processFileService.analyzeDxfFile(file, coordinate, limit);

        if (Boolean.FALSE.equals(result.get("success"))) {
            throw new RecipeException(ErrorCode.FILE_PARSE_FAILED,
                    (String) result.getOrDefault("message", "DXF 분석 결과가 비어있습니다."));
        }

        return ResponseEntity.ok(DefaultApiResponse.success(result));
    }

    /**
     * [Helper] 파일 유효성 검사 (보안)
     */
    private void validateFile(MultipartFile file) {
        if (file == null || file.isEmpty()) {
            throw new RecipeException(ErrorCode.FILE_EMPTY);
        }
        String filename = file.getOriginalFilename();
        if (filename != null && filename.contains("..")) {
            throw new RecipeException(ErrorCode.FILE_NAME_INVALID, "파일명에 상위 경로(..)가 포함될 수 없습니다.");
        }
    }
}