package incheon.product.geoview3d.data3d.web;

import incheon.com.cmm.api.DefaultApiResponse;
import incheon.product.common.geo3d.ExternalApiClient;
import incheon.product.common.geo3d.GeoView3DProperties;
import incheon.product.geoview3d.data3d.service.Data3DService;
import incheon.product.geoview3d.data3d.vo.Data3DModelVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
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 javax.validation.Valid;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

/**
 * GeoView-3D 3D 데이터 관리 API. 디지털트윈 3D 모델의 CRUD 및 상태 관리를 담당한다.
 */
@Slf4j
@RestController
@RequestMapping("/api/v1/product/g3d/data3d")
public class Data3DApiController {

    /** 허용 경로 패턴: 알파벳/숫자/슬래시/하이픈/언더스코어/점만 허용, 연속 점(..) 금지 */
    private static final Pattern SAFE_PATH_PATTERN = Pattern.compile("^[a-zA-Z0-9가-힣/_\\-.]+$");

    @Resource(name = "productData3DService")
    private Data3DService data3DService;

    @Resource(name = "geoView3DProperties")
    private GeoView3DProperties properties;

    @Resource(name = "productExternalApiClient")
    private ExternalApiClient externalApiClient;

    @GetMapping("")
    public DefaultApiResponse<List<Data3DModelVO>> getList(Data3DModelVO vo) {
        List<Data3DModelVO> list = data3DService.getList(vo);
        int totalCount = list.size();

        DefaultApiResponse<List<Data3DModelVO>> response = DefaultApiResponse.success(list);
        response.addMeta("totalCount", totalCount);
        response.addMeta("page", vo.getPageIndex());
        response.addMeta("size", vo.getPageUnit());
        return response;
    }

    @GetMapping("/{id}")
    public DefaultApiResponse<Data3DModelVO> getById(@PathVariable Integer id,
                                                      @RequestParam(defaultValue = "2") String srvcSeCd) {
        return DefaultApiResponse.success(data3DService.getById(id, srvcSeCd));
    }

    @PostMapping("")
    public DefaultApiResponse<Data3DModelVO> create(@Valid @RequestBody Data3DModelVO vo) {
        Data3DModelVO created = data3DService.create(vo);
        return DefaultApiResponse.success(created);
    }

    @PutMapping("/{id}")
    public DefaultApiResponse<Void> update(@PathVariable Integer id, @Valid @RequestBody Data3DModelVO vo) {
        vo.setDgtlPairMdlId(id);
        data3DService.update(vo);
        return DefaultApiResponse.success("수정되었습니다.");
    }

    @DeleteMapping("/{id}")
    public DefaultApiResponse<Void> delete(@PathVariable Integer id,
                                            @RequestParam(defaultValue = "2") String srvcSeCd) {
        data3DService.delete(id, srvcSeCd);
        return DefaultApiResponse.success("삭제되었습니다.");
    }

    @PutMapping("/status")
    public DefaultApiResponse<Void> updateStatus(@RequestParam Integer dgtlPairMdlId,
                                                  @RequestParam String status,
                                                  @RequestParam String serviceName,
                                                  @RequestHeader("X-API-Key") String apiKey) {
        String configuredKey = properties.getExternalApi().getApiKey();
        if (!StringUtils.hasText(apiKey) || !apiKey.equals(configuredKey)) {
            return DefaultApiResponse.error(403, "유효하지 않은 API 키입니다.", "FORBIDDEN");
        }

        data3DService.updateStatus(dgtlPairMdlId, status, serviceName);
        return DefaultApiResponse.success("상태가 변경되었습니다.");
    }

    @GetMapping("/check-path")
    public DefaultApiResponse<Map<String, Object>> checkPath(@RequestParam String path) {
        Map<String, Object> result = new HashMap<>();
        result.put("path", path);
        result.put("valid", false);

        // SSRF 방어: 경로 트래버설 및 비허용 문자 차단
        if (!StringUtils.hasText(path) || path.contains("..") || !SAFE_PATH_PATTERN.matcher(path).matches()) {
            result.put("message", "유효하지 않은 경로입니다.");
            return DefaultApiResponse.success(result);
        }

        String managerUrl = properties.getGisManager().getManagerUrl();
        if (StringUtils.hasText(managerUrl)) {
            try {
                ResponseEntity<String> response = externalApiClient.exchangeForGis(
                        managerUrl + "/api/storage/check?path=" + java.net.URLEncoder.encode(path, java.nio.charset.StandardCharsets.UTF_8),
                        HttpMethod.GET,
                        null,
                        String.class
                );
                result.put("valid", response.getStatusCode().is2xxSuccessful());
                result.put("message", response.getBody() != null ? response.getBody() : "");
            } catch (Exception e) {
                log.warn("경로 확인 실패 - path: {}", path, e);
                result.put("valid", false);
                result.put("message", e.getMessage());
            }
        } else {
            result.put("message", "GIS Manager가 설정되지 않았습니다.");
        }

        return DefaultApiResponse.success(result);
    }
}
