package incheon.cmm.g3d.DbMngList.service.impl;

import com.fasterxml.jackson.databind.ObjectMapper;
import incheon.cmm.g3d.DbMngList.mapper.G3DDbMngMapper;
import incheon.cmm.g3d.DbMngList.service.G3DDbMngService;
import incheon.cmm.g3d.DbMngList.vo.G3DDbMngVO;
import incheon.cmm.g3f.util.GisManagerUtils;
import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.web.client.RestTemplate;

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

@Slf4j
@Service
public class G3DDbMngServiceImpl implements G3DDbMngService {
    @Autowired private ObjectMapper objectMapper;

    private final G3DDbMngMapper mapper;
    private final RestTemplate restTemplate;
    private final TransactionTemplate transactionTemplate;
    @Value("${gis.build.url}")
    private String THREE_D_MANAGER_URL;
    @Value("$gis.build.prefix}")
    private String prefix;
    @Value("${gis.manager.url}")
    private String MANAGER_URL;
    public G3DDbMngServiceImpl(G3DDbMngMapper mapper, RestTemplate restTemplate, 
                               TransactionTemplate transactionTemplate) {
        this.mapper = mapper;
        this.restTemplate = restTemplate;
        this.transactionTemplate = transactionTemplate;
        this.transactionTemplate.setReadOnly(false);
    }

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

    @Override
    public List<G3DDbMngVO> getList(String searchKeyword, String categoryFilter, String searchType, int page, int size, String srvcNum, String dgtlPairGroupCd) {
        int offset = (page - 1) * size;
        Map<String, Object> params = new HashMap<>();
        params.put("srvcNum", srvcNum);
        params.put("categoryFilter", categoryFilter);
        params.put("searchKeyword", searchKeyword);
        params.put("dgtlPairGroupCd", dgtlPairGroupCd);
        params.put("searchType", searchType);
        params.put("size", size);
        params.put("offset", offset);
        return mapper.findList(params);
    }

    @Override
    public int getTotalCount(String searchKeyword, String categoryFilter, String searchType, String srvcNum, String dgtlPairGroupCd) {
        Map<String, Object> params = new HashMap<>();
        params.put("srvcNum", srvcNum);
        params.put("categoryFilter", categoryFilter);
        params.put("searchKeyword", searchKeyword);
        params.put("dgtlPairGroupCd", dgtlPairGroupCd);
        params.put("searchType", searchType);
        return mapper.getTotalCount(params);
    }

    @Override
    public G3DDbMngVO getById(Integer dgtlPairMdlId, String srvcSeCd) {
        Map<String, Object> params = new HashMap<>();
        params.put("dgtlPairMdlId", dgtlPairMdlId);
        params.put("srvcSeCd", srvcSeCd);
        return mapper.findById(params);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void create(G3DDbMngVO vo) {
        Integer apiId = null;
        String srvcSeCd = vo.getSrvcSeCd();
        String target;
        try {
            if ("1".equals(srvcSeCd)) {
                apiId = createToStorageApi(vo);
                
                if (apiId == null) {
                    log.error("Storage API 호출 실패 - ID가 null로 반환됨");
                    throw new RuntimeException("Storage API 호출 실패: ID를 받지 못했습니다");
                }
                
            } else if ("2".equals(srvcSeCd)) {
                Map<String, Object> res = createToAssetApi(vo);
                apiId = convertToInteger(res.get("id"));
                
                if (apiId == null) {
                    log.error("Asset API 호출 실패 - ID가 null로 반환됨");
                    throw new RuntimeException("Asset API 호출 실패: ID를 받지 못했습니다");
                }
                if (res.get("url") != null) {
                    String url = res.get("url").toString();

                    Map<String, String> mapping = Map.of(
                        "root/incheon/base/tileset/",   "/MapPrime3DManager/root/incheon/base/tileset/",
                        "root/incheon/base/terrain/",  "/MapPrime3DManager/root/incheon/base/terrain/",
                        "root/incheon/base/pointcloud/", "/MapPrime3DManager/root/incheon/base/pointcloud/"
                    );

                    String serviceUrl = url;

                    for (Map.Entry<String, String> entry : mapping.entrySet()) {
                        if (serviceUrl.contains(entry.getKey())) {
                            serviceUrl = serviceUrl.replace(entry.getKey(), entry.getValue());
                        }
                    }

                    vo.setDgtlPairMdlSrvcNm(serviceUrl);
                }



            } else {
                throw new IllegalArgumentException("잘못된 srvcSeCd: " + srvcSeCd);
            }
            
            vo.setDgtlPairMdlId(apiId);
            log.info("API 호출 성공 (srvcSeCd={}): ID={}", srvcSeCd, apiId);

            int result = mapper.insert(vo);

            if (result != 1) {
                log.error("DB 등록 실패 - API ID: {}, srvcSeCd: {}", apiId, srvcSeCd);
                
                executeCompensation(srvcSeCd, apiId);
                
                throw new RuntimeException("DB 등록 실패");
            }

            log.info("등록 성공 (srvcSeCd={}): ID={}, url={}", 
                     srvcSeCd, apiId, vo.getDgtlPairMdlSrvcNm());

        } catch (Exception e) {
            log.error("등록 중 오류 발생 (srvcSeCd={}, apiId={})", srvcSeCd, apiId, e);
            if (apiId != null) {
                executeCompensation(srvcSeCd, apiId);
            }
            throw new RuntimeException("등록 실패: " + e.getMessage(), e);
        }
    }
    @Override
    public void update(G3DDbMngVO vo) {
        transactionTemplate.execute(status -> {
            try {
                Map<String, Object> params = new HashMap<>();
                params.put("dgtlPairMdlId", vo.getDgtlPairMdlId());
                params.put("srvcSeCd", vo.getSrvcSeCd());
                
                G3DDbMngVO existing = mapper.findById(params);
                if (existing == null) {
                    throw new IllegalArgumentException("존재하지 않는 모델 (ID: " + vo.getDgtlPairMdlId() + ", srvcSeCd: " + vo.getSrvcSeCd() + ")");
                }

                if (vo.getDgtlPairCtgry() == null) {
                    vo.setDgtlPairCtgry(existing.getDgtlPairCtgry());
                }
                if (vo.getDgtlPairMdlSrvcNm() == null) {
                    vo.setDgtlPairMdlSrvcNm(existing.getDgtlPairMdlSrvcNm());
                }

                if ("1".equals(vo.getSrvcSeCd())) {
                    updateToStorageApi(vo);
                } else if ("2".equals(vo.getSrvcSeCd())) {
                    updateToAssetApi(vo);
                }

                mapper.update(vo);
                log.info("수정 성공 (ID: {}, srvcSeCd: {})", vo.getDgtlPairMdlId(), vo.getSrvcSeCd());
                return null;
            } catch (Exception e) {
                log.error("수정 중 오류", e);
                status.setRollbackOnly();
                throw new RuntimeException("수정 실패: " + e.getMessage(), e);
            }
        });
    }
    @Override
    public int statUpdate(G3DDbMngVO vo) {
        Integer result = transactionTemplate.execute(status -> {
            try {
                int updated = mapper.statUpdate(vo);
                log.info("상태 업데이트 완료 - ID: {}, 업데이트 건수: {}", 
                         vo.getDgtlPairMdlId(), updated);
                return updated;  // int 반환
            } catch (Exception e) {
                log.error("상태 업데이트 중 오류 - ID: {}", vo.getDgtlPairMdlId(), e);
                status.setRollbackOnly();
                throw new RuntimeException("상태 업데이트 실패: " + e.getMessage(), e);
            }
        });
        
        return result != null ? result : 0;  // null 체크 후 반환
    }

    @Override
    public void delete(Integer dgtlPairMdlId, String srvcSeCd) {
        transactionTemplate.execute(status -> {
            try {
                if ("1".equals(srvcSeCd)) {
                    deleteFromStorageApi(dgtlPairMdlId);
                } else if ("2".equals(srvcSeCd)) {
                    deleteFromAssetApi(dgtlPairMdlId);
                }
                
                Map<String, Object> params = new HashMap<>();
                params.put("dgtlPairMdlId", dgtlPairMdlId);
                params.put("srvcSeCd", srvcSeCd);
                mapper.delete(params);
                
                log.info("삭제 성공 (ID: {}, srvcSeCd: {})", dgtlPairMdlId, srvcSeCd);
                return null;
            } catch (Exception e) {
                log.error("삭제 중 오류", e);
                status.setRollbackOnly();
                throw new RuntimeException("삭제 실패: " + e.getMessage(), e);
            }
        });
    }

    private Integer createToStorageApi(G3DDbMngVO vo) {
        try {
            GisManagerUtils api = new GisManagerUtils(restTemplate, THREE_D_MANAGER_URL);
            Map<String, Object> req = new HashMap<>();
            req.put("method", vo.getDgtlPairGroupCd());
            req.put("name", vo.getDgtlPairMdlNm());
            req.put("tag", vo.getDgtlPairMdlExpln());
            req.put("workspace", "default");
            req.put("path", vo.getDgtlPairMdlOrgnlPath());

            log.info("=== Storage API 요청 시작 ===");
            log.info("URL: {}", THREE_D_MANAGER_URL);
            log.info("요청 데이터: {}", req);
            
            Map<String, Object> res = api.createStorage(req);
            
            log.info("=== Storage API 응답 수신 ===");
            log.info("응답 전체: {}", res);
            log.info("응답 타입: {}", res != null ? res.getClass().getName() : "null");
            
            if (res == null || res.isEmpty()) {
                log.error("Storage API 응답이 null 또는 비어있음");
                throw new RuntimeException("Storage API 응답이 없습니다. 3D Manager 서버를 확인하세요.");
            }

            //  ID 추출 시도 (여러 가능성 고려)
            Object idObj = res.get("id");
            log.info("추출된 ID 객체: {} (타입: {})", idObj, idObj != null ? idObj.getClass().getName() : "null");
            
            Integer id = convertToInteger(idObj);
            
            if (id == null) {
                log.error("=== Storage API ID 변환 실패 ===");
                log.error("원본 ID 값: {}", idObj);
                log.error("응답의 모든 키: {}", res.keySet());
                log.error("응답의 모든 값: {}", res.values());
                
                //  다른 키 이름 시도
                if (res.containsKey("ID")) {
                    id = convertToInteger(res.get("ID"));
                    log.warn("대문자 'ID' 키로 변환 시도: {}", id);
                } else if (res.containsKey("dgtl_pair_mdl_id")) {
                    id = convertToInteger(res.get("dgtl_pair_mdl_id"));
                    log.warn("'dgtl_pair_mdl_id' 키로 변환 시도: {}", id);
                }
                
                if (id == null) {
                    throw new RuntimeException(
                        "Storage API가 유효한 ID를 반환하지 않았습니다. " +
                        "응답: " + res.toString()
                    );
                }
            }
            
            log.info("=== Storage API 호출 성공 ===");
            log.info("생성된 ID: {}", id);
            
            return id;
            
        } catch (Exception e) {
            log.error("=== Storage API 호출 중 예외 발생 ===", e);
            log.error("예외 타입: {}", e.getClass().getName());
            log.error("예외 메시지: {}", e.getMessage());
            
            if (e instanceof RuntimeException) {
                throw (RuntimeException) e;
            }
            throw new RuntimeException("Storage API 연동 실패: " + e.getMessage(), e);
        }
    }

    private void updateToStorageApi(G3DDbMngVO vo) {
        GisManagerUtils api = new GisManagerUtils(restTemplate, THREE_D_MANAGER_URL);
        Map<String, Object> data = new HashMap<>();
        data.put("name", vo.getDgtlPairMdlNm());
        data.put("tag", vo.getDgtlPairMdlExpln());
        data.put("workspace", "incheon");
        api.updateStorage(vo.getDgtlPairMdlId(), data);
        log.info("Storage API 업데이트 완료 (ID: {})", vo.getDgtlPairMdlId());
    }

    private boolean deleteFromStorageApi(Integer id) {
        try {
            GisManagerUtils api = new GisManagerUtils(restTemplate, THREE_D_MANAGER_URL);
            boolean result = api.deleteStorage(id);
            if (result) {
                log.info("Storage API 삭제 성공 (ID: {})", id);
            } else {
                log.warn("Storage API 삭제 실패 (ID: {})", id);
            }
            return result;
        } catch (Exception e) {
            log.error("Storage API 삭제 중 오류 (ID: {})", id, e);
            return false;
        }
    }

    private Map<String, Object> createToAssetApi(G3DDbMngVO vo) {
        try {
            String json = vo.getDgtlPairMdlTrsfOption();
            Map<String, Object> parsed = objectMapper.readValue(json, Map.class);
            Map<String, Object> assetData = new HashMap<>();
            
            // 필수 필드만 추가
            assetData.put("method", parsed.get("method"));
            assetData.put("name", parsed.get("name"));
            assetData.put("tag", parsed.get("tag"));
            assetData.put("category", parsed.get("category"));
            assetData.put("workspace", parsed.get("workspace"));
            assetData.put("input", parsed.get("input"));
            assetData.put("output", parsed.getOrDefault("output", ""));
            assetData.put("srvcGroupCd", parsed.get("srvcGroupCd"));  
            assetData.put("srvcClsfCd", parsed.get("srvcClsfCd"));    
            // buildOptions 처리
            Object buildOpts = parsed.get("buildOptions");
            if (buildOpts == null) {
                buildOpts = parsed.get("buildoptions");
            }
            
            if (buildOpts != null && buildOpts instanceof Map) {
                Map<String, Object> ops = (Map<String, Object>) buildOpts;
                
                // SRID 정리 (EPSG: 제거)
                if (ops.containsKey("srid")) {
                    String srid = ops.get("srid").toString();
                    srid = srid.replace("EPSG:", "").replace("epsg:", "").trim();
                    ops.put("srid", srid);
                }
                
                // format 기본값 처리
                if (ops.containsKey("format")) {
                    String f = ops.get("format").toString().trim();
                    if (f.isEmpty()) f = "*";
                    ops.put("format", f);
                }
                
                assetData.put("buildoptions", ops);
            }
            assetData.remove("dgtlPairMdlSrvcNm");
            assetData.remove("url");

            // 최종 요청 로그
            log.info("===== FINAL ASSET API REQUEST =====");
            log.info("URL: {}/api/asset", THREE_D_MANAGER_URL);
            log.info("Body:\n{}", objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(assetData));

            // API 호출
            Map<String, Object> res = new GisManagerUtils(restTemplate, THREE_D_MANAGER_URL)
                .createAsset(assetData);

            // ID 변환 및 URL 추출
            res.put("id", convertToInteger(res.get("id")));
            
            return res;

        } catch (Exception e) {
            log.error("Asset API 등록 실패", e);
            throw new RuntimeException("Asset API 등록 실패: " + e.getMessage(), e);
        }
    }





    private void updateToAssetApi(G3DDbMngVO vo) {
        GisManagerUtils api = new GisManagerUtils(restTemplate, THREE_D_MANAGER_URL);
        Map<String, Object> req = new HashMap<>();
        req.put("name", vo.getDgtlPairMdlNm());
        req.put("tag", vo.getDgtlPairMdlExpln());
        req.put("category", vo.getDgtlPairCtgry());
        req.put("workspace", vo.getDgtlPairMdlSrvcNm());
        req.put("srvcGroupCd", vo.getSrvcGroupCd()); 
        req.put("srvcClsfCd", vo.getSrvcClsfCd());
        api.updateAsset(vo.getDgtlPairMdlId(), req);
        log.info("Asset API 업데이트 완료 (ID: {})", vo.getDgtlPairMdlId());
    }

    private boolean deleteFromAssetApi(Integer id) {
        try {
            GisManagerUtils api = new GisManagerUtils(restTemplate, THREE_D_MANAGER_URL);
            boolean result = api.deleteAsset(id);
            if (result) {
                log.info("Asset API 삭제 성공 (ID: {})", id);
            } else {
                log.warn("Asset API 삭제 실패 (ID: {})", id);
            }
            return result;
        } catch (Exception e) {
            log.error("Asset API 삭제 중 오류 (ID: {})", id, e);
            return false;
        }
    }

    private Integer convertToInteger(Object obj) {
        if (obj == null) {
            log.warn("convertToInteger: 입력값이 null");
            return null;
        }
        
        try {
            if (obj instanceof Number) {
                int result = ((Number) obj).intValue();
                log.debug("Number -> Integer 변환 성공: {}", result);
                return result;
            }
            
            if (obj instanceof String) {
                String str = ((String) obj).trim();
                if (str.isEmpty()) {
                    log.warn("convertToInteger: 빈 문자열");
                    return null;
                }
                int result = Integer.parseInt(str);
                log.debug("String -> Integer 변환 성공: {}", result);
                return result;
            }
            
            log.warn("convertToInteger: 지원하지 않는 타입 - {}", obj.getClass().getName());
            
        } catch (NumberFormatException e) {
            log.error("convertToInteger: 숫자 변환 실패 - 입력값: {}", obj, e);
        }
        
        return null;
    }
    
    private void executeCompensation(String srvcSeCd, Integer apiId) {
        if (apiId == null) {
            log.warn("insert 실패: apiId가 없음");
            return;
        }
        try {
            if ("1".equals(srvcSeCd)) {
                boolean deleted = deleteFromStorageApi(apiId);
                log.warn("트랜잭션 실행: Storage API 삭제 (id={}, 결과={})", apiId, deleted);
                
            } else if ("2".equals(srvcSeCd)) {
                boolean deleted = deleteFromAssetApi(apiId);
                log.warn("트랜잭션 실행: Asset API 삭제 (id={}, 결과={})", apiId, deleted);
            }
            
        } catch (Exception ex) {
            log.error("보상 트랜잭션 실패 (id={}, srvcSeCd={}): {}", 
                     apiId, srvcSeCd, ex.getMessage(), ex);
        }
    }
    @Override
    public Map<String, Object> checkStoragePath(String path) {
        String normalizedPath = path.replace("\\", "/");
        log.info("[Storage Path Check] path={}", normalizedPath);

        try {
            GisManagerUtils api = new GisManagerUtils(restTemplate, MANAGER_URL);
            return api.checkStoragePath(normalizedPath);
        } catch (Exception e) {
            log.error("Storage 경로 확인 실패", e);
            Map<String, Object> result = new HashMap<>();
            result.put("statusCode", 500);
            result.put("success", false);
            result.put("message", "경로 확인 실패: " + e.getMessage());
            return result;
        }
    }

    
}