package incheon.product.geoview3d.traffic.web;

import incheon.com.cmm.api.DefaultApiResponse;
import incheon.com.cmm.exception.BusinessException;
import incheon.product.geoview3d.traffic.service.CctvService;
import incheon.product.geoview3d.traffic.service.TrafficService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;

/**
 * 교통/CCTV REST API 컨트롤러.
 *
 * <pre>
 * 엔드포인트:
 *   GET /api/v1/product/g3d/traffic           - 교통 정보 조회 (bbox)
 *   GET /api/v1/product/g3d/traffic/roads      - 도로명 검색 (페이징)
 *   GET /api/v1/product/g3d/traffic/links      - 도로명별 링크 조회
 *   GET /api/v1/product/g3d/traffic/cctv       - CCTV 목록 조회 (bbox)
 *   GET /api/v1/product/g3d/traffic/cctv/redirect - CCTV 영상 URL 리다이렉트 해석
 * </pre>
 */
@Slf4j
@RestController
@RequestMapping("/api/v1/product/g3d/traffic")
public class TrafficApiController {

    @Resource(name = "productTrafficService")
    private TrafficService trafficService;

    private static final double BBOX_MAX_RANGE = 1.0;
    private static final int MAX_PAGE_SIZE = 500;

    @Resource(name = "productCctvService")
    private CctvService cctvService;

    /**
     * bbox 범위 내 교통 정보 조회.
     *
     * @param roadType 도로 유형
     * @param minX     최소 경도
     * @param minY     최소 위도
     * @param maxX     최대 경도
     * @param maxY     최대 위도
     * @return 링크별 교통 등급/속도 목록
     */
    @GetMapping("")
    public ResponseEntity<DefaultApiResponse<List<Map<String, Object>>>> getTrafficInfo(
            @RequestParam(required = false) String roadType,
            @RequestParam double minX,
            @RequestParam double minY,
            @RequestParam double maxX,
            @RequestParam double maxY) {

        validateBbox(minX, minY, maxX, maxY);
        List<Map<String, Object>> result = trafficService.getTrafficInfo(roadType, minX, minY, maxX, maxY);
        return ResponseEntity.ok(DefaultApiResponse.success(result));
    }

    /**
     * 도로명 검색 (페이징).
     *
     * @param roadType   도로 유형
     * @param searchName 검색 도로명
     * @param page       페이지 번호
     * @param size       페이지 크기
     * @return 도로명 목록과 총 건수
     */
    @GetMapping("/roads")
    public ResponseEntity<DefaultApiResponse<Map<String, Object>>> getRoadNames(
            @RequestParam(required = false) String roadType,
            @RequestParam(required = false) String searchName,
            @RequestParam(defaultValue = "1") int page,
            @RequestParam(defaultValue = "10") int size) {

        page = Math.max(1, page);
        size = Math.min(Math.max(size, 1), MAX_PAGE_SIZE);
        Map<String, Object> result = trafficService.getRoadNames(roadType, searchName, page, size);
        return ResponseEntity.ok(DefaultApiResponse.success(result));
    }

    /**
     * 도로명에 해당하는 링크 목록 조회.
     *
     * @param roadName 도로명
     * @return 링크 목록
     */
    @GetMapping("/links")
    public ResponseEntity<DefaultApiResponse<List<Map<String, Object>>>> getLinks(
            @RequestParam String roadName) {

        List<Map<String, Object>> result = trafficService.getLinks(roadName);
        return ResponseEntity.ok(DefaultApiResponse.success(result));
    }

    /**
     * bbox 범위 내 CCTV 목록 조회.
     *
     * @param gid  특정 CCTV GID (선택)
     * @param minX 최소 경도
     * @param minY 최소 위도
     * @param maxX 최대 경도
     * @param maxY 최대 위도
     * @return CCTV 목록
     */
    @GetMapping("/cctv")
    public ResponseEntity<DefaultApiResponse<List<Map<String, Object>>>> getCctvList(
            @RequestParam(required = false) Long gid,
            @RequestParam double minX,
            @RequestParam double minY,
            @RequestParam double maxX,
            @RequestParam double maxY) {

        validateBbox(minX, minY, maxX, maxY);
        List<Map<String, Object>> result = cctvService.getCctvList(gid, minX, minY, maxX, maxY);
        return ResponseEntity.ok(DefaultApiResponse.success(result));
    }

    /**
     * CCTV 영상 URL 리다이렉트 해석.
     *
     * @param url 원본 CCTV 영상 URL
     * @return 리다이렉트 해석된 최종 URL
     */
    @GetMapping("/cctv/redirect")
    public ResponseEntity<DefaultApiResponse<String>> getCctvRedirectUrl(
            @RequestParam String url) {

        String resolvedUrl = cctvService.getCctvRedirectUrl(url);
        return ResponseEntity.ok(DefaultApiResponse.success(resolvedUrl));
    }

    private void validateBbox(double minX, double minY, double maxX, double maxY) {
        if (minX >= maxX || minY >= maxY) {
            throw new BusinessException("잘못된 범위: min 값이 max 값보다 클 수 없습니다.", HttpStatus.BAD_REQUEST);
        }
        if ((maxX - minX) > BBOX_MAX_RANGE || (maxY - minY) > BBOX_MAX_RANGE) {
            throw new BusinessException("조회 범위가 너무 넓습니다. 최대 " + BBOX_MAX_RANGE + "도 이내로 요청하세요.", HttpStatus.BAD_REQUEST);
        }
    }
}
