package incheon.ags.mrb.upload.web;

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

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import org.geotools.api.filter.Filter;
import org.geotools.filter.text.cql2.CQL;
import org.geotools.filter.text.cql2.CQLException;
import org.egovframe.rte.psl.dataaccess.util.EgovMap;

import incheon.ags.mrb.analysis.mapper.AnalysisHistoryMapper;
import incheon.ags.mrb.main.mapper.BoardObjectMapper;
import incheon.ags.mrb.main.mapper.RecipeLayerMapper;
import incheon.ags.mrb.upload.mapper.IncheonServiceMapper;
import incheon.ags.mrb.upload.service.ProcessFileService;
import incheon.com.cmm.api.DefaultApiResponse;
import incheon.com.security.vo.LoginVO;

@Controller
@RequestMapping("/ags/mrb/user/layer")
public class UserLayerController {

    private final static Logger logger = LoggerFactory.getLogger(UserLayerController.class);

	private final IncheonServiceMapper mapper;
	private final ProcessFileService processFileService;
	private final AnalysisHistoryMapper analysisHistoryMapper;
	private final RecipeLayerMapper recipeLayerMapper;
	private final BoardObjectMapper boardObjectMapper;
	
	@Autowired
	public UserLayerController(IncheonServiceMapper mapper,
							 ProcessFileService processFileService,
							 AnalysisHistoryMapper analysisHistoryMapper,
							 RecipeLayerMapper recipeLayerMapper,
							 BoardObjectMapper boardObjectMapper) {
		this.mapper = mapper;
		this.processFileService = processFileService;
		this.analysisHistoryMapper = analysisHistoryMapper;
		this.recipeLayerMapper = recipeLayerMapper;
		this.boardObjectMapper = boardObjectMapper;
	}
	
	/**
	 * 현재 로그인된 사용자 ID를 조회한다.
	 * 
	 * @return 사용자 ID, 없으면 "system"
	 */
	private String getCurrentUserId() {
		Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
		if (authentication != null && authentication.getPrincipal() instanceof LoginVO) {
			LoginVO loginVO = (LoginVO) authentication.getPrincipal();
			return loginVO.getUserId();
		}
		return "system"; // 인증 정보가 없을 때 기본값
	}
	
	@GetMapping("/g2flist.do")
	@ResponseBody
	public ResponseEntity<DefaultApiResponse<Map<String, Object>>> getFileListAsIncheon(
	    @RequestParam(required = false) String searchKeyword,
	    @RequestParam(required = false) String lyrClsfCd,  // 그룹 분류 코드 필터
	    @RequestParam(required = false) String spceTy,     // 공간 타입 필터
	    @RequestParam(required = false, defaultValue = "1") int pageNumber,
	    @RequestParam(required = false, defaultValue = "20") int pageSize) {

	    Map<String, Object> result = new HashMap<>();
	    
	    // 현재 로그인한 사용자 ID 가져오기
	    String currentUserId = getCurrentUserId();

	    int offset = (pageNumber - 1) * pageSize;

	    List<Map<String, Object>> layers;
	    int totalCount;

	    // 필터 조건 생성
	    Map<String, Object> filterParams = new HashMap<>();
	    filterParams.put("userId", currentUserId);
	    filterParams.put("searchKeyword", searchKeyword);
	    filterParams.put("lyrClsfCd", lyrClsfCd);
	    filterParams.put("spceTy", spceTy);
	    filterParams.put("offset", offset);
	    filterParams.put("pageSize", pageSize);

	    // 필터가 있는 경우 필터링된 조회
	    layers = mapper.selectUserLayersWithFilters(filterParams);
	    totalCount = mapper.selectUserLayerCountWithFilters(filterParams);

	    result.put("content", layers);
	    result.put("totalElements", totalCount);
	    result.put("pageNumber", pageNumber);
	    result.put("pageSize", pageSize);
	    result.put("totalPages", (int) Math.ceil((double)totalCount / pageSize));

	    return ResponseEntity.ok(DefaultApiResponse.success(result));
	}
    
    @PostMapping("/edit.do")
    public ResponseEntity<DefaultApiResponse<Map<String, Object>>> loadLayer(@RequestParam(value = "id") Long id) {
        Map<String, Object> response = new HashMap<>();
        try {
            String layerName = mapper.selectLayerNameById(id);
            String layerType = mapper.selectLayerTypeById(id);
            String srid = mapper.selectSridById(id);
            
            List<Map<String, Object>> layerProperties = mapper.selectColumnsExcludeGeomByLayerName(layerName);
            
            List<Map<String, Object>> filtered = layerProperties.stream().filter(m -> {
                        Object cn = m.get("columnName");
                        return cn != null && !"gid".equals(cn.toString());
            }).toList();

            response.put("success", true);
            response.put("layerName", layerName);
            response.put("layerType", layerType);
            response.put("layerProperties", filtered);
            response.put("srid", srid);

            return ResponseEntity.ok(DefaultApiResponse.success(response));
        } catch (RuntimeException e) {
            logger.error(e.getMessage(), e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body(DefaultApiResponse.error(500, "컬럼 조회 실패", e.getMessage()));
        }
    }

    @PostMapping("/delete.do")
    @ResponseBody
    @Transactional
    public ResponseEntity<DefaultApiResponse<Map<String, Object>>> deleteFile(@RequestParam("id") Long id) {
        try {
            String tableName = mapper.selectLayerNameById(id);

            if (tableName == null || tableName.isBlank()) {
                return ResponseEntity.badRequest()
                        .body(DefaultApiResponse.error(400, "레이어를 찾을 수 없습니다.", "Layer not found"));
            }

            // 1. 해당 레이어를 참조하는 분석 이력이 있는지 확인
            List<Integer> historyIds = analysisHistoryMapper.selectHstryIdsByUserLyrId(id);

            if (!historyIds.isEmpty()) {
                // 2. 분석 이력이 있다면, user_lyr_id를 NULL로 설정하고 상태를 DELETED로 변경 (Soft Delete)
                analysisHistoryMapper.softDeleteHstriesByUserLyrId(id);
            }

            // 3. 삭제할 레시피 레이어 ID 목록 조회
            List<Integer> recipeLayerIds = recipeLayerMapper.selectRecipeLayerIdsByUserLayerId(id.intValue());

            // 4. 삭제될 보드 오브젝트 정보 조회 (프론트엔드 UI 업데이트용)
            List<Map<String, Object>> deletedBoardObjects = new java.util.ArrayList<>();
            if (!recipeLayerIds.isEmpty()) {
                for (Integer recipeLyrId : recipeLayerIds) {
                    List<incheon.ags.mrb.main.vo.BoardObjectVO> boardObjects =
                            boardObjectMapper.selectBoardObjectListByRecipeLyrId(recipeLyrId);
                    for (incheon.ags.mrb.main.vo.BoardObjectVO bo : boardObjects) {
                        Map<String, Object> boInfo = new HashMap<>();
                        boInfo.put("boarObjectId", bo.getBoarObjectId());
                        boInfo.put("boarTy", bo.getBoarTy());
                        deletedBoardObjects.add(boInfo);
                    }
                }
            }

            // 5. 보드 오브젝트 삭제 (레시피 레이어를 참조하므로 먼저 삭제)
            if (!recipeLayerIds.isEmpty()) {
                boardObjectMapper.deleteBoardObjectsByRecipeLayerIds(recipeLayerIds);
            }

            // 6. 레시피 레이어 삭제
            recipeLayerMapper.deleteRecipeLayersByUserLayerId(id.intValue());

            // 7. user_lyr 테이블에서 레이어 메타데이터 삭제 (트랜잭션)
            mapper.deleteLayerNameById(id);

            // 7-1. [추가] 시퀀스 의존성 확인 및 제거
            // 다른 테이블이 현재 삭제하려는 테이블의 시퀀스를 참조하고 있는지 확인
            List<Map<String, String>> dependencies = mapper.selectDependentTables(tableName);
            if (dependencies != null && !dependencies.isEmpty()) {
                for (Map<String, String> dep : dependencies) {
                    String depSchema = dep.get("schemaName");
                    String depTable = dep.get("tableName");
                    String depColumn = dep.get("columnName");
                    
                    logger.info("Drop dependency default: {}.{}.{}", depSchema, depTable, depColumn);
                    mapper.dropColumnDefault(depSchema, depTable, depColumn);
                }
            }

            // 8. 실제 공간 데이터 테이블 삭제 (트랜잭션)
            mapper.dropLayerTable(tableName);

            // 9. MapPrime 서버에서 레이어 삭제 (DB 트랜잭션 커밋 후 실행되는 것이 이상적이나, 여기선 순차 실행)
            processFileService.deleteLayer("incheon", tableName);

            // 결과 데이터 구성
            Map<String, Object> result = new HashMap<>();
            result.put("deletedBoardObjects", deletedBoardObjects); 

            return ResponseEntity.ok(DefaultApiResponse.success(result, "레이어가 성공적으로 삭제되었습니다."));
        } catch (Exception e) {
            logger.error("레이어 삭제 중 오류 발생", e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body(DefaultApiResponse.error(500, "레이어 삭제 중 오류가 발생했습니다.", e.getMessage()));
        }
    }


    
    /**
     * 나의 레이어를 참조하는 레시피 목록 조회
     * @param id 나의 레이어 ID (user_lyr_id)
     * @return 레시피 목록
     */
    @GetMapping("/recipes.do")
    @ResponseBody
    public ResponseEntity<DefaultApiResponse<Map<String, Object>>> getRecipesByLayerId(@RequestParam("id") Integer id) {
        try {
            Map<String, Object> result = new HashMap<>();
            List<EgovMap> recipes = recipeLayerMapper.selectRecipesByUserLayerId(id);
            
            result.put("recipes", recipes);
            result.put("count", recipes.size());
            
            return ResponseEntity.ok(DefaultApiResponse.success(result));
        } catch (RuntimeException e) {
            logger.error("참조 레시피 조회 실패", e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body(DefaultApiResponse.error(500, "나의 레이어를 참조하는 레시피 목록 조회 실패", e.getMessage()));
        }
    }
    
    /**
     * CQL 필터 표현식 검증
     * @param request CQL 표현식이 포함된 요청 맵
     * @return 검증 결과
     */
    @PostMapping("/validate-cql.do")
    @ResponseBody
    public ResponseEntity<DefaultApiResponse<Map<String, Object>>> validateCqlFilter(@RequestBody Map<String, Object> request) {
        Map<String, Object> result = new HashMap<>();
        try {
            String cqlExpression = (String) request.get("cqlExpression");
            
            if (cqlExpression == null || cqlExpression.trim().isEmpty()) {
                result.put("valid", true);
                return ResponseEntity.ok(DefaultApiResponse.success(result, "빈 표현식은 유효합니다."));
            }
            
            // CQL 표현식 검증
            try {
                CQL.toFilter(cqlExpression);
            } catch (CQLException e) {
                // 문법 오류가 있는 경우 (HTTP 200 OK 처리하되 valid false 반환)
                result.put("valid", false);
                return ResponseEntity.ok(DefaultApiResponse.success(result, "잘못된 CQL 필터 형식입니다. 표현식을 확인해주세요."));
            }
            
            result.put("valid", true);
            return ResponseEntity.ok(DefaultApiResponse.success(result, "CQL 표현식이 유효합니다."));
            
        } catch (RuntimeException e) {
            logger.error("CQL 검증 중 서버 오류", e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body(DefaultApiResponse.error(500, "CQL 검증 중 오류가 발생했습니다.", e.getMessage()));
        }
    }
}