package incheon.cmm.g2f.layer.web;

import javax.validation.Valid;

import incheon.cmm.g2f.layer.vo.*;
import incheon.com.cmm.api.DefaultApiResponse;
import incheon.com.security.util.SecurityUtil;
import incheon.com.security.vo.LoginVO;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import org.geotools.ows.ServiceException;
import org.geotools.ows.wmts.WebMapTileServer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;

import lombok.RequiredArgsConstructor;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.*;

import incheon.cmm.g2f.layer.service.G2FLayerService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;

import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * G2F 레이어 API 컨트롤러
 */
@Tag(name = "G2F 공통 API")
@RestController
@RequestMapping("/cmm/g2f/layer")
@RequiredArgsConstructor
public class G2FLayerController {
    
    private static final Logger logger = LoggerFactory.getLogger(G2FLayerController.class);
    
    private final G2FLayerService g2FLayerService;

    /**
     * 레이어 편집 API
     * Added/Modified/Deleted 통합 처리
     */
    @Operation(summary = "레이어 편집", description = "공간 데이터 레이어의 피처들을 일괄 편집합니다.")
    @ApiResponses(value = {
        @ApiResponse(responseCode = "200", description = "편집 성공",
                    content = @Content(schema = @Schema(implementation = DefaultApiResponse.class))),
        @ApiResponse(responseCode = "400", description = "요청 파라미터 오류"),
        @ApiResponse(responseCode = "404", description = "레이어를 찾을 수 없음"),
        @ApiResponse(responseCode = "500", description = "서버 내부 오류")
    })
    @PostMapping("/edit")
    public ResponseEntity<DefaultApiResponse<LayerEditResultVO>> editLayer(
            @Valid @RequestBody LayerEditRequestDTO request) {

        logger.info("레이어 편집 요청 - layerId: {}, layerType: {}, added: {}, modified: {}, deleted: {}",
                request.getLayerId(),
                request.getLayerType().toString(),
                request.getChanges().getAdded().size(),
                request.getChanges().getModified().size(),
                request.getChanges().getDeleted().size());
        
        try {
            // 현재 사용자 ID 조회
            String currentUserId = getCurrentUserId();
            
            // 비즈니스 로직 처리
            LayerEditResultVO result = g2FLayerService.editLayer(request, currentUserId);

            logger.info("레이어 편집 성공 - layerId: {}, layerType: {}, 처리결과: added={}, modified={}, deleted={}",
                    result.getLayerId(),
                    request.getLayerType().toString(),
                    result.getProcessed().getAdded(),
                    result.getProcessed().getModified(),
                    result.getProcessed().getDeleted());
            
            // ApiResponse 사용으로 간단하게 성공 응답 생성
            return ResponseEntity.ok(DefaultApiResponse.success(result, "레이어 편집이 완료되었습니다."));
            
        } catch (IllegalArgumentException e) {
            logger.warn("레이어 편집 실패 - 잘못된 요청", e);
            
            // 존재하지 않는 레이어 ID인 경우 404 반환
            if (e.getMessage().contains("레이어를 찾을 수 없습니다")) {
                return ResponseEntity.status(404)
                        .body(DefaultApiResponse.error(404, "레이어 편집 실패 - 잘못된 요청", "LAYER_NOT_FOUND"));
            }
            
            return ResponseEntity.badRequest()
                    .body(DefaultApiResponse.error(400, "레이어 편집 실패 - 잘못된 요청", "INVALID_REQUEST"));
            
        } catch (SecurityException e) {
            logger.warn("레이어 편집 실패 - 권한 없음", e);
            
            return ResponseEntity.status(403)
                    .body(DefaultApiResponse.error(403, "레이어 편집 실패 - 권한 없음", "ACCESS_DENIED"));
            
        } catch (Exception e) {
            logger.error("레이어 편집 실패 - 서버 오류", e);
            
            return ResponseEntity.internalServerError()
                    .body(DefaultApiResponse.error(500, "서버 내부 오류가 발생했습니다.", "SERVER_ERROR"));
        }
    }

    /**
     * 업무 레이어 조회
     */
    @Operation(summary = "업무 레이어 조회", description = "업무 레이어를 조회합니다.")
    @ApiResponses(value = {
            @ApiResponse(responseCode = "200", description = "조회 성공",
                    content = @Content(schema = @Schema(implementation = DefaultApiResponse.class))),
            @ApiResponse(responseCode = "400", description = "요청 파라미터 오류"),
            @ApiResponse(responseCode = "404", description = "레이어를 찾을 수 없음"),
            @ApiResponse(responseCode = "500", description = "서버 내부 오류")
    })
    @GetMapping("/task")
    public ResponseEntity<DefaultApiResponse<Map<String, Object>>> getTaskLayerList(TaskLayerSearchRequestDTO searchVO) {
        if (searchVO.getPageIndex() < 1) {
            searchVO.setPageIndex(1);
        }

        Set<String> removedGroupCd = new HashSet<>();
        if (!searchVO.getSkipAuthorization()) {
            if (!SecurityUtil.hasPermission("UIS", "PERM_DATA_UCF")) {
                removedGroupCd.add("ucf");
            }
            if (!SecurityUtil.hasPermission("UIS", "PERM_DATA_UEF")) {
                removedGroupCd.add("uef");
            }
            if (!SecurityUtil.hasPermission("UIS", "PERM_DATA_UGF")) {
                removedGroupCd.add("ugf");
            }
            if (!SecurityUtil.hasPermission("UIS", "PERM_DATA_URF")) {
                removedGroupCd.add("urf");
            }
            if (!SecurityUtil.hasPermission("UIS", "PERM_DATA_USF")) {
                removedGroupCd.add("usf");
            }
            if (!SecurityUtil.hasPermission("UIS", "PERM_DATA_UUF")) {
                removedGroupCd.add("uuf");
            }
            if (!SecurityUtil.hasPermission("UIS", "PERM_DATA_UWF")) {
                removedGroupCd.add("uwf");
            }
            if(!SecurityUtil.canAccessSystem("RPS")){
                removedGroupCd.add("res");
            }
        }
        List<TaskLayerVO> content  = g2FLayerService.selectTaskLayerList(searchVO, removedGroupCd);


        Map<String, Object> response = new HashMap<>();
        response.put("content", content);
        if(searchVO.getPageSize() > 1) {
            response.put("page", searchVO.getPageIndex());
            response.put("size", searchVO.getPageSize());

            long total = g2FLayerService.selectTaskLayerListTotCnt(searchVO, removedGroupCd);
            response.put("totalElements", total);
            response.put("totalPages", (int) Math.ceil((double) total / searchVO.getPageSize()));
        }

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

    /**
     * 항공 사진 레이어 조회
     */
    @Operation(summary = "항공 사진 레이어 조회", description = "항공 사진 레이어를 조회합니다.")
    @ApiResponses(value = {
            @ApiResponse(responseCode = "200", description = "조회 성공",
                    content = @Content(schema = @Schema(implementation = DefaultApiResponse.class))),
            @ApiResponse(responseCode = "400", description = "요청 파라미터 오류"),
            @ApiResponse(responseCode = "404", description = "레이어를 찾을 수 없음"),
            @ApiResponse(responseCode = "500", description = "서버 내부 오류")
    })
    @GetMapping("/flight")
    public ResponseEntity<DefaultApiResponse<Map<String, Object>>> getFlightPhotoLayerList(FlightPhotoLayerSearchRequestDTO searchVO) {
        if (searchVO.getPageIndex() < 1) {
            searchVO.setPageIndex(1);
        }

        // 보안 항공 영상 활용 권한 확인
        boolean otsdRlsEn = SecurityUtil.hasPermission("AGS", "PERM_FUNC_FLIGHT_DATA");

        List<FlightPhotoLyrVO> content = null;
        content = g2FLayerService.selectFlightPhotoLayerList(searchVO, otsdRlsEn);

        Map<String, Object> response = new HashMap<>();
        response.put("content", content);
        if(searchVO.getPageSize() > 1) {
            response.put("page", searchVO.getPageIndex());
            response.put("size", searchVO.getPageSize());

            long total = g2FLayerService.selectFlightPhotoLayerListTotCnt(searchVO, otsdRlsEn);
            response.put("totalElements", total);
            response.put("totalPages", (int) Math.ceil((double) total / searchVO.getPageSize()));
        }

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

    /**
     * 항공 사진 레이어 다운로드
     */
    @PostMapping("/flight/download")
    public ResponseEntity<StreamingResponseBody> getFlightPhotoLayerDownload(@Valid @RequestBody FlightPhotoLayerDownloadRequestDTO request) {

        boolean hasPermission = SecurityUtil.hasPermission("AGS", "PERM_FUNC_FLIGHT_DATA");

        String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
        String filename = "flight_images_" + timestamp + ".zip";

        HttpHeaders headers = new HttpHeaders();
        String encoded = URLEncoder.encode(filename, StandardCharsets.UTF_8);
        headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename*=UTF-8''" + encoded);
        headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
        headers.add(HttpHeaders.CACHE_CONTROL, "no-cache");

        WebMapTileServer wmts;
        try {
            g2FLayerService.validateFlightPhotoLayer(request);
            wmts = g2FLayerService.createWebMapTileServer();
        } catch (ServiceException | IOException e) {
            logger.error(e.getMessage(), e);
            return ResponseEntity.internalServerError().body(null);
        } catch (IllegalArgumentException e) {
            logger.error(e.getMessage(), e);
            return ResponseEntity.badRequest().body(null);
        }

        StreamingResponseBody stream = outputStream -> {
            try {
                g2FLayerService.writeFlightPhotoLayerZip(outputStream, request, hasPermission, wmts);
            } catch (ServiceException e) {
                throw new RuntimeException(e);
            }
        };

        return ResponseEntity.ok()
                .headers(headers)
                .body(stream);
    }
    
    /**
     * 현재 사용자 ID 조회
     */
    private String getCurrentUserId() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication != null && authentication.getPrincipal() instanceof LoginVO) {
            LoginVO loginVO = (LoginVO) authentication.getPrincipal();
            return loginVO.getUserId();
        }

        return null;
    }
}