package incheon.ags.dss.regen.service.impl;

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

import incheon.ags.dss.regen.service.AnalysisService;
import incheon.ags.dss.regen.service.UrbFcltyAnlsService;
import incheon.com.cmm.context.RequestContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import incheon.ags.dss.regen.mapper.UrbTrgtClsfMapper;
import incheon.ags.dss.regen.service.UrbTrgtClsfService;
import incheon.ags.dss.regen.vo.UrbTrgtClsfMstVO;
import incheon.ags.dss.regen.vo.UrbTrgtClsfDtlVO;
import incheon.ags.dss.regen.vo.UrbFcltyAnlsMstVO; // UrbFcltyAnlsMstVO 임포트 추가
import incheon.ags.dss.regen.mapper.UrbFcltyAnlsMapper; // UrbFcltyAnlsMapper 임포트 추가

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;

@Service("urbTrgtClsfService")
@RequiredArgsConstructor
@Slf4j
public class UrbTrgtClsfServiceImpl implements UrbTrgtClsfService {

    private final UrbTrgtClsfMapper urbTrgtClsfMapper;
    private final UrbFcltyAnlsMapper urbFcltyAnlsMapper; // UrbFcltyAnlsMapper 주입

    @Autowired
    @Lazy
    private AnalysisService analysisService;

    // [추가] 분석 로직 호출을 위해 Service 주입 (Mapper 호출 대신 사용)
    private final UrbFcltyAnlsService urbFcltyAnlsService;

    @Override
    public List<UrbTrgtClsfMstVO> selectUrbTrgtClsfMstList(UrbTrgtClsfMstVO vo) throws Exception {
        return urbTrgtClsfMapper.selectUrbTrgtClsfMstList(vo);
    }

    @Override
    public int selectUrbTrgtClsfMstListCnt(UrbTrgtClsfMstVO vo) throws Exception {
        return urbTrgtClsfMapper.selectUrbTrgtClsfMstListCnt(vo);
    }

    @Override
    @Transactional
    public void insertUrbTrgtClsfMst(UrbTrgtClsfMstVO vo) throws Exception {
        urbTrgtClsfMapper.insertUrbTrgtClsfMst(vo);

        // 대상분류 생성 시점에 1:1 시설분석(ACEI) 마스터도 함께 생성
        if (vo.getZoneNo() == null) {
            throw new IllegalArgumentException("zoneNo 값이 필요합니다.");
        }
        UrbFcltyAnlsMstVO anlsMstVO = new UrbFcltyAnlsMstVO();
        anlsMstVO.setZoneNo(vo.getZoneNo());
        anlsMstVO.setTrgtClsfNo(vo.getTrgtClsfNo());
        anlsMstVO.setMainUsgCd("ACEI");
        anlsMstVO.setRadu(BigDecimal.valueOf(100)); // 초기 기본값
        urbFcltyAnlsMapper.insertUrbFcltyAnlsMst(anlsMstVO);
    }

    @Override
    @Transactional
    public void updateUrbTrgtClsfMst(UrbTrgtClsfMstVO vo) throws Exception {
        if (vo.getTrgtClsfNo() == null) {
            throw new IllegalArgumentException("trgtClsfNo 값이 필요합니다.");
        }
        if (vo.getTrgtClsfNm() == null || vo.getTrgtClsfNm().trim().isEmpty()) {
            throw new IllegalArgumentException("trgtClsfNm 값이 필요합니다.");
        }
        urbTrgtClsfMapper.updateUrbTrgtClsfMst(vo);
    }

    @Override
    @Transactional
    public void deleteUrbTrgtClsfMst(UrbTrgtClsfMstVO vo) throws Exception {
        // 1:1 접근도(ACEI) 분석 마스터/상세도 함께 정리
        if (vo.getTrgtClsfNo() != null) {
            UrbFcltyAnlsMstVO searchVO = new UrbFcltyAnlsMstVO();
            searchVO.setTrgtClsfNo(vo.getTrgtClsfNo());

            UrbFcltyAnlsMstVO aceiMst = urbFcltyAnlsMapper.selectAceiAnlsMstOne(searchVO);
            if (aceiMst != null && aceiMst.getFcltAnlsNo() != null) {
                urbFcltyAnlsMapper.deleteUrbFcltyAnlsDtl(Integer.valueOf(aceiMst.getFcltAnlsNo()));
                urbFcltyAnlsMapper.deleteUrbFcltyAnlsMst(Integer.valueOf(aceiMst.getFcltAnlsNo()));
            }
        }

        // 대상분류 상세 먼저 삭제 (FK 무결성)
        if (vo.getTrgtClsfNo() != null) {
            UrbTrgtClsfDtlVO dtlVO = new UrbTrgtClsfDtlVO();
            dtlVO.setTrgtClsfNo(vo.getTrgtClsfNo());
            urbTrgtClsfMapper.deleteUrbTrgtClsfDtlByClsfNo(dtlVO);
        }
        urbTrgtClsfMapper.deleteUrbTrgtClsfMst(vo);
    }

    @Override
    public UrbTrgtClsfDtlVO selectUrbTrgtParcelByPoint(Map<String, Object> param) throws Exception {
        return urbTrgtClsfMapper.selectUrbTrgtParcelByPoint(param);
    }

    @Override
    @Transactional
    public void insertUrbTrgtClsfDtl(UrbTrgtClsfDtlVO vo) throws Exception {
        urbTrgtClsfMapper.insertUrbTrgtClsfDtl(vo);
    }

    @Override
    public List<UrbTrgtClsfDtlVO> selectUrbTrgtClsfDtlList(UrbTrgtClsfDtlVO vo) throws Exception {
        return urbTrgtClsfMapper.selectUrbTrgtClsfDtlList(vo);
    }

    @Override
    public int selectUrbTrgtClsfDtlListCnt(UrbTrgtClsfDtlVO vo) throws Exception {
        return urbTrgtClsfMapper.selectUrbTrgtClsfDtlListCnt(vo);
    }

    @Override
    @Transactional
    public void deleteUrbTrgtClsfDtlItem(Integer trgtClsfDtlNo) throws Exception {
        urbTrgtClsfMapper.deleteUrbTrgtClsfDtlItem(trgtClsfDtlNo);
    }

    @Override
    @Transactional
    public void deleteUrbTrgtClsfDtlByClsfNo(UrbTrgtClsfDtlVO vo) throws Exception {
        urbTrgtClsfMapper.deleteUrbTrgtClsfDtlByClsfNo(vo);
    }

    /**
     * 접근성 분석 실행
     * @param vo UrbTrgtClsfDtlVO
     * @return 생성된 분석 마스터 번호 (fcltAnlsNo)
     * @throws Exception
     */
    @Override
    @Transactional
    public int runAccessibilityAnalysis(UrbTrgtClsfDtlVO vo) throws Exception {
        log.info("Running Accessibility Analysis for Target Classification No: {}", vo.getTrgtClsfNo());

        // [수정] 사용자 ID 확보 (VO에 없으면 세션에서 가져옴)
        String userId = vo.getLOGIN_USER_ID();
        if (userId == null || userId.isEmpty()) {
            userId = RequestContext.getCurrentUserId();
            if (userId == null) userId = "ANONYMOUS"; // 비로그인/테스트 대비
        }
        vo.setLOGIN_USER_ID(userId);

        // 1) 1:1 ACEI 분석 마스터 조회 (없으면 생성)
        UrbFcltyAnlsMstVO searchVO = new UrbFcltyAnlsMstVO();
        searchVO.setTrgtClsfNo(vo.getTrgtClsfNo());
        searchVO.setLOGIN_USER_ID(userId); // 조회 시에도 ID 필요

        UrbFcltyAnlsMstVO aceiMst = urbFcltyAnlsMapper.selectAceiAnlsMstOne(searchVO);

        if (aceiMst == null) {
            UrbFcltyAnlsMstVO anlsMstVO = new UrbFcltyAnlsMstVO();
            anlsMstVO.setZoneNo(vo.getZoneNo());
            anlsMstVO.setTrgtClsfNo(vo.getTrgtClsfNo());
            anlsMstVO.setRadu(BigDecimal.valueOf(vo.getRadu()));
            anlsMstVO.setMainUsgCd("ACEI");
            anlsMstVO.setPrcsCmptnYn("N");

            // [수정] ID 세팅
            anlsMstVO.setUserId(userId);
            anlsMstVO.setLOGIN_USER_ID(userId);

            urbFcltyAnlsMapper.insertUrbFcltyAnlsMst(anlsMstVO);
            aceiMst = anlsMstVO;
        } else {
            // 기존 마스터를 재사용하고, 실행 파라미터(구역/반경)를 최신 값으로 갱신
            aceiMst.setZoneNo(vo.getZoneNo());
            aceiMst.setRadu(BigDecimal.valueOf(vo.getRadu()));
            aceiMst.setPrcsCmptnYn("N"); // 리셋

            // [수정] ID 세팅 (Update 쿼리의 WHERE절 통과 및 감사로그용)
            aceiMst.setUserId(userId);
            aceiMst.setLOGIN_USER_ID(userId);

            urbFcltyAnlsMapper.updateAceiAnlsMst(aceiMst);

            // 재실행 시 기존 결과는 정리 후 재계산
            urbFcltyAnlsMapper.deleteUrbFcltyAnlsDtl(Integer.valueOf(aceiMst.getFcltAnlsNo()));
        }

        int anlsNo = Integer.parseInt(aceiMst.getFcltAnlsNo());

        // 2) 비동기 분석 실행
        if (TransactionSynchronizationManager.isActualTransactionActive()) {
            TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
                @Override
                public void afterCommit() {
                    analysisService.executeAnalysisInJava(anlsNo);
                }
            });
        } else {
            analysisService.executeAnalysisInJava(anlsNo);
        }

        return anlsNo;
    }

    /**
     * [Java Conversion] 대상 분류 생성 로직 구현
     * - sp_run_urb_trgt 대체
     */
    @Override
    @Transactional
    public int createTargetClassificationInJava(UrbTrgtClsfMstVO vo) throws Exception {
        log.info("Creating Target Classification (Java) for Zone: {}, Code: {}", vo.getZoneNo(), vo.getMdlAtrbCd());

        // 1. Master 등록
        // vo에는 zoneNo, trgtClsfNm, mdlAtrbCd, userDsgnYn, userId 등이 담겨있어야 함
        if (vo.getUserDsgnYn() == null) vo.setUserDsgnYn("N"); // 기본값 시스템생성(N)

        urbTrgtClsfMapper.insertUrbTrgtClsfMst(vo); // keyProperty="trgtClsfNo"로 ID 생성됨
        int trgtClsfNo = vo.getTrgtClsfNo();

        // 2. 대상 시설물 추출 (공간 연산 + 속성 필터링)
        Map<String, Object> params = new HashMap<>();
        params.put("radu", vo.getRadu());
        params.put("zoneNo", vo.getZoneNo());
        params.put("mainUsgCd", vo.getMdlAtrbCd()); // VO의 필드명에 맞게 매핑 (DB 컬럼: main_usg_cd)

        log.info("Fetching target facilities...");
        List<UrbTrgtClsfDtlVO> candidates = urbTrgtClsfMapper.selectTargetFacilitiesForCreation(params);

        // 3. Batch Insert
        if (candidates != null && !candidates.isEmpty()) {
            log.info("Inserting {} target facilities...", candidates.size());

            // 데이터 보정 (FK 및 감사정보 설정)
            for (UrbTrgtClsfDtlVO dtl : candidates) {
                dtl.setTrgtClsfNo(trgtClsfNo);
                dtl.setFrstRegId(vo.getFrstRegId());   // Master의 등록자 ID 상속
                dtl.setLastMdfcnId(vo.getLastMdfcnId());
            }

            // 대량 데이터 처리를 위한 분할 Insert (예: 1000건씩)
            int batchSize = 1000;
            for (int i = 0; i < candidates.size(); i += batchSize) {
                List<UrbTrgtClsfDtlVO> batch = candidates.subList(i, Math.min(i + batchSize, candidates.size()));
                urbTrgtClsfMapper.insertUrbTrgtClsfDtlBatch(batch);
            }
        } else {
            log.info("No facilities found for the given criteria.");
        }

        return trgtClsfNo;
    }
}
