package incheon.cmm.g2f.TaskLayer.service.impl;

import org.egovframe.rte.fdl.cmmn.EgovAbstractServiceImpl;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;

import incheon.cmm.g2f.TaskLayer.vo.G2TaskLayerVO;
import incheon.cmm.g2f.TaskLayer.service.G2FTaskLayerService;
import incheon.cmm.g2f.TaskLayer.mapper.G2FTaskLayerMapper;
import incheon.cmm.g2f.util.GisServerRestUtils;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
@Service
public class G2FTaskLayerServiceImpl extends EgovAbstractServiceImpl implements G2FTaskLayerService {
    
    private final G2FTaskLayerMapper mapper;
    private final RestTemplate restTemplate;
    
    @Value("${gis.server.url}")
    private String baseUrl;
    
    @Value("${gis.server.prefix}")
    private String prefix;

    public G2FTaskLayerServiceImpl(G2FTaskLayerMapper mapper, RestTemplate restTemplate) {
        this.mapper = mapper;
        this.restTemplate = restTemplate;
    }

    @Override
    public G2TaskLayerVO getById(Integer nfId) {
        return mapper.findById(nfId);
    }

    @Override
    public List<G2TaskLayerVO> getList() {
        return mapper.findAll();
    }

    @Override
    public List<G2TaskLayerVO> getList(String searchKeyword, String searchType, int page, int size) {
        int offset = (page - 1) * size;
        return mapper.findAllWithPaging(searchKeyword, searchType, size, offset);
    }

    @Override
    public int getTotalCount(String searchKeyword, String searchType) {
        return mapper.count(searchKeyword, searchType);
    }

    /**
     * 신규 레이어 등록
     * GIS 웹서버 등록이 먼저 성공한 후 DB에 저장
     */
    @Override
    @Transactional
    public void create(G2TaskLayerVO layer) {
        // GIS 웹서버 등록이 먼저 성공해야 DB에 저장
        publishToGisServer(layer);
        mapper.insert(layer);
    }

    /**
     * 레이어 수정
     * GIS 웹서버 수정이 먼저 성공한 후 DB 업데이트
     */
    @Override
    @Transactional
    public void update(G2TaskLayerVO layer) {
        // 기존 데이터 조회 (서비스명 변경 감지용)
        G2TaskLayerVO oldLayer = mapper.findById(layer.getTaskLyrId());
        
        // 서비스명이 변경되었는지 확인
        boolean isServiceNameChanged = oldLayer != null && 
                                       !oldLayer.getLyrSrvcNm().equals(layer.getLyrSrvcNm());
        
        if (isServiceNameChanged) {
            deleteFromGisServer(oldLayer);
            publishToGisServer(layer);
        } else {
            updateToGisServer(layer);
        }
        
        mapper.update(layer);
    }

    @Override
    @Transactional
    public void Metaupdate(G2TaskLayerVO layer) {
    	mapper.update(layer);
    }

    /**
     * 레이어 삭제
     */
    @Override
    @Transactional
    public void delete(Integer nfId) {
        G2TaskLayerVO layer = mapper.findById(nfId);
        if (layer != null) {
            deleteFromGisServer(layer);
        }
        mapper.delete(nfId);
        log.info("레이어 삭제 완료: ID={}", nfId);
    }

    /**
     * GIS 웹서버에 레이어 등록
     */
    private void publishToGisServer(G2TaskLayerVO layer) {
        try {
            String[] parts = layer.getLyrPhysNm().split("\\.");
            if (parts.length != 2) {
                throw new IllegalArgumentException("레이어 물리명 형식이 올바르지 않습니다: " + layer.getLyrPhysNm());
            }

            String storage = parts[0];
            String tableName = parts[1];
            String workspace = layer.getLyrSrvcPrefix();
            String name = layer.getLyrSrvcNm();
            Integer srid = layer.getCntm() != null ? layer.getCntm().intValue() : 3857;

            GisServerRestUtils gisServer = new GisServerRestUtils(restTemplate, baseUrl);

            boolean success = gisServer.publishLayer(
                    workspace,
                    name,
                    storage,
                    tableName,
                    srid,
                    null, null, null, null 
            );

            if (!success) {
                throw new RuntimeException("GIS 웹서버 레이어 등록 실패: " + name);
            }

            log.info("GIS 웹서버 등록 성공: {}", name);

        } catch (IllegalArgumentException e) {
            log.error("입력 유효성 오류", e);
            throw e;
        } catch (RestClientException e) {
            log.error("GIS 웹서버 통신 오류: {}", e.getMessage(), e);
            throw new RuntimeException("GIS 웹서버 통신 오류 발생", e);
        } catch (Exception e) {
            log.error("레이어 등록 중 알 수 없는 오류 발생", e);
            throw new RuntimeException("GIS 등록 중 예외 발생", e);
        }
    }

    /**
     * GIS 웹서버에 수정 요청
     */
    private void updateToGisServer(G2TaskLayerVO layer) {
        String workspace = layer.getLyrSrvcPrefix();
        String name = layer.getLyrSrvcNm();
        Integer srid = layer.getCntm() != null ? layer.getCntm().intValue() : 3857;

        GisServerRestUtils gisServer = new GisServerRestUtils(restTemplate, baseUrl);

        // 레이어 존재 여부 확인
        boolean layerExists = checkLayerExists(gisServer, workspace, name);

        if (layerExists) {
            // 레이어가 존재하면 수정
            log.info("GIS 웹서버 레이어 수정 시도: {}/{}", workspace, name);
            boolean success = gisServer.updateLayer(
                    workspace,
                    name,
                    srid,
                    null, null, null, null
            );

            if (!success) {
                throw new RuntimeException("GIS 웹서버 레이어 수정 실패: " + name);
            }
            log.info("GIS 웹서버 수정 성공: {}", name);

        } else {
            // 레이어가 존재하지 않으면 새로 생성
            log.warn("GIS 웹서버에 레이어가 존재하지 않아 새로 생성합니다: {}/{}", workspace, name);
            publishToGisServer(layer);
        }
    }

    /**
     * GIS 웹서버에 레이어가 존재하는지 확인
     */
    private boolean checkLayerExists(GisServerRestUtils gisServer, String workspace, String name) {
        try {
            Map<String, Object> layerInfo = gisServer.getLayerInfo(workspace, name);
            boolean exists = layerInfo != null && !layerInfo.isEmpty();
            log.debug("레이어 존재 여부: {}/{} = {}", workspace, name, exists);
            return exists;
            
        } catch (RestClientException e) {
            // 404 등의 에러가 발생하면 레이어가 없는 것으로 판단
            log.warn("레이어 조회 실패 (존재하지 않을 가능성): {}/{}", workspace, name);
            return false;
        } catch (Exception e) {
            log.error("레이어 존재 여부 확인 중 예외 발생: {}/{}", workspace, name, e);
            return false;
        }
    }

    /**
     * GIS 웹서버에서 삭제
     */
    private void deleteFromGisServer(G2TaskLayerVO layer) {
        String workspace = layer.getLyrSrvcPrefix();
        String name = layer.getLyrSrvcNm();

        GisServerRestUtils gisServer = new GisServerRestUtils(restTemplate, baseUrl);

        boolean success = gisServer.deleteLayer(workspace, name);

        if (!success) {
            log.warn("GIS 웹서버 레이어 삭제 실패 또는 존재하지 않음: {}", name);
        } else {
            log.info("GIS 웹서버 레이어 삭제 성공: {}", name);
        }
    }
    
    @Override
    public Map<String, Object> verifyGeometryTable(String schema, String tableNm, String lyrSrvcNm, String lyrPhysNm) {
        Map<String, Object> params = new HashMap<>();
        params.put("schema", schema);
        params.put("tableNm", tableNm);
        params.put("lyrSrvcNm", lyrSrvcNm);
        params.put("lyrPhysNm", lyrPhysNm);

        String lyrPhyNm = schema + "." + tableNm;
        params.put("lyrPhyNm", lyrPhyNm);

        List<String> errors = new ArrayList<>();
        Map<String, Object> result = new HashMap<>();

        // 1. geometry_columns 테이블 확인
        List<Map<String, Object>> rows = mapper.verifyGeometryTable(schema, tableNm);

        if (rows == null || rows.size() == 0) {
            errors.add("시스템에 해당 테이블이 존재하지 않습니다.");
        } else if (rows.size() > 1) {
            errors.add("geometry_columns 에 동일 테이블 정의가 2개 이상 존재합니다.");
        } else {
            // 정상 1개인 경우
            Map<String, Object> row = rows.get(0);

            String type = (String) row.get("type");
            Integer srid = (Integer) row.get("srid");

            // JSP 표시용 데이터 저장
            result.put("geomType", type);
            result.put("srid", srid);
        }

        // 2. 업무레이어 물리명 중복 체크
        Integer countPhy = mapper.verifyTaskPhyTable(params);
        if (countPhy != null && countPhy > 0) {
            errors.add("업무 시스템에 동일한 물리명이 이미 존재합니다.");
        }

        // 3. 업무레이어 서비스명 중복 체크
        Integer countSrv = mapper.verifyTaskSrvTable(params);
        if (countSrv != null && countSrv > 0) {
            errors.add("업무 시스템에 동일한 서비스명이 이미 존재합니다.");
        }

        // 4. GIS 웹서버 체크
        GisServerRestUtils gisServer = new GisServerRestUtils(restTemplate, baseUrl);
        String workspace = prefix;
        String name = tableNm;

        Map<String, Object> layerInfo = gisServer.getLayerInfo(workspace, name);

        if (layerInfo != null && !layerInfo.isEmpty()) {
            errors.add("GIS 웹서버에 동일한 레이어가 이미 등록되어 있습니다.");
        }

        // 최종 결과
        result.put("success", errors.isEmpty());
        result.put("errors", errors);

        return result;
    }

    
 // G2FTaskLayerServiceImpl.java
    @Override
    public List<Map<String, Object>> getGroupCodes() {
        return mapper.selectGroupCodes();
    }
}