package com.icomsys.main_vm.biz.rcp.conversation.service;

import com.icomsys.main_vm.biz.advice.excep.CustomBadRequestException;
import com.icomsys.main_vm.biz.advice.excep.CustomNoSuchFieldException;
import com.icomsys.main_vm.biz.advice.excep.CustomNotFoundException;
import com.icomsys.main_vm.biz.advice.excep.CustomRuntimeException;
import com.icomsys.main_vm.biz.common.common.service.LogService;
import com.icomsys.main_vm.biz.common.common.service.LogVO;
import com.icomsys.main_vm.biz.common.login.service.LoginService;
import com.icomsys.main_vm.biz.common.system.vo.DslDataTableRes;
import com.icomsys.main_vm.biz.rcp.conversation.vo.*;
import com.icomsys.main_vm.biz.rcp.util.ScenarioConverter;
import com.icomsys.main_vm.common.code.code.LongTextManagerColumnCode;
import com.icomsys.main_vm.common.code.code.LongTextManagerTableCode;
import com.icomsys.main_vm.common.code.enumresource.ActionResource;
import com.icomsys.main_vm.common.code.enumresource.MenuResource;
import com.icomsys.main_vm.db.jpa.entity.scenario.*;
import com.icomsys.main_vm.db.jpa.entity.scenario.id.TbLongtextManagerId;
import com.icomsys.main_vm.db.jpa.entity.system.TbBotUser;
import com.icomsys.main_vm.db.jpa.entity.system.TbServiceGrouop;
import com.icomsys.main_vm.db.jpa.repo.scenario.*;
import com.icomsys.main_vm.db.jpa.repo.system.TbBotUserRepo;
import com.icomsys.main_vm.db.jpa.repo.system.TbIcsLogRepo;
import com.icomsys.main_vm.db.jpa.repo.system.TbServiceGrouopRepo;
import com.querydsl.core.QueryResults;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;


import java.security.NoSuchAlgorithmException;
import java.time.LocalDateTime;

import java.util.Collections;
import java.util.List;

/**
 * Name : ConversationScenarioGUIService date : 2023-01-27 author :  Mong description : =========================================================== DATE                 AUTHOR                NOTE
 * ----------------------------------------------------------- 2023-01-27                Mong             최초 생성
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class ConversationScenarioGUIService {

    private final TbLongtextManagerRepo tbLongtextManagerRepo;
    private final TbScenarioMasterRepo tbScenarioMasterRepo;
    private final TbScenarioReleaseRepo tbScenarioReleaseRepo;
    private final TbScenarioHistoryRepo tbScenarioHistoryRepo;
    private final TbServiceGrouopRepo tbServiceGrouopRepo;
    private final TbBotUserRepo tbBotUserRepo;
    private final TbIcsLogRepo tbIcsLogRepo;
    private final TbScenarioRelatedIntentRepo tbScenarioRelatedIntentRepo;

    private final LoginService loginService;
    private final LogService logService;


    public int insertDev(LongtextManagerVO dto) {
        log.info("dto -> {}", dto);

        TbLongtextManager vo = new TbLongtextManager();
        vo.setColumnCode(dto.getColumnCode());
        vo.setRfSeq(dto.getRfSeq());
        vo.setTableCode(dto.getTableCode());
        vo.setTextContents(dto.getTextContents());
        vo.setChecksum("Y");
        tbLongtextManagerRepo.save(vo);
        int result = 1;
        return result;
    }

    @Transactional(readOnly = true)
    public LongtextManagerVO selectDev(TbLongtextManagerId dto) throws CustomNotFoundException {
        TbLongtextManager result = tbLongtextManagerRepo.findById(dto)
                .orElseThrow(() -> new CustomNotFoundException());
        return new LongtextManagerVO().ofTbLongtextManager(result);
    }

    public DslDataTableRes<ConversationScenarioManagerRes>  searchScenarioList(ConversationScenarioManagerSearchReq dto){
        QueryResults<ConversationScenarioManagerRes> result = tbScenarioMasterRepo.searchScenarioList(dto);
        return new DslDataTableRes<ConversationScenarioManagerRes>(result);
    }

    public DslDataTableRes<ConversationScenarioReleaseRes>  searchScenarioReleaseList(ConversationScenarioManagerSearchReq dto) {
        QueryResults<ConversationScenarioReleaseRes> result = tbScenarioMasterRepo.searchScenarioReleaseList(dto);
        return new DslDataTableRes<ConversationScenarioReleaseRes>(result);
    }

    @Transactional
    public int scenarioRegister(ConversationScenarioMasterVO dto) throws CustomNotFoundException, CustomBadRequestException {
        String userId = loginService.getUserVo().getUserId();
        LocalDateTime currentDateTime = LocalDateTime.now();
        String oprMngCode = dto.getCustCode() + "_RCP_" +  dto.getServiceGroup();

        TbServiceGrouop sg = tbServiceGrouopRepo.findByServiceGroupAndCustCode(dto.getServiceGroup(), dto.getCustCode())
                .orElseThrow(() -> new CustomNotFoundException("시나리오 그룹이 없습니다."));

//        시나리오 공통을 M 타입으로 등록 관리하도록 정책 변경
//        if(sg.getServiceType().equals("M")){
//            throw new CustomBadRequestException("Master 시나리오 그룹에는 시나리오를 등록할 수 없습니다.");
//        }

        if(dto.getCustCode() == null || dto.getCustCode().equals("")){
            throw new CustomBadRequestException("custCode 값이 비었습니다.");
        }

        TbScenarioMaster vo = new TbScenarioMaster();
        vo.setOprMngCode(oprMngCode);
        vo.setScenarioName(dto.getScenarioName());
        vo.setUseYn("Y");
        vo.setRegistId(userId);
        vo.setRegistDate(currentDateTime);
        vo.setUpdateId(userId);
        vo.setUpdateDate(currentDateTime);

        tbScenarioMasterRepo.save(vo);

        logService.LogSave(LogVO.builder()
                .menuAuth(MenuResource.rcpconversationscenariomanage)
                .actionCode(ActionResource.insert.getName())
                .registDate(currentDateTime)
                .attribute1(Long.toString(vo.getSeq()))
                .remark( vo.getScenarioName() + " 시나리오를 등록하였습니다.")
                .serviceGroup(dto.getServiceGroup())
                .build());
        return 0;
    }

    @Transactional
    public int scenarioNameUpdate(ConversationScenarioMasterVO dto) throws CustomNotFoundException, CustomBadRequestException {
        String userId = loginService.getUserVo().getUserId();
        LocalDateTime currentDateTime = LocalDateTime.now();

        if (dto.getScenarioName() == null || dto.getScenarioName().isEmpty()) {
            throw new CustomBadRequestException("시나리오 이름이 비었습니다");
        }

        TbScenarioMaster scMaster = tbScenarioMasterRepo.findBySeqAndUseYn(dto.getSeq(), "Y")
                .orElseThrow(() -> new CustomNotFoundException("시나리오 정보가 없습니다"));

        scMaster.setScenarioName(dto.getScenarioName());
        scMaster.setUpdateId(userId);
        scMaster.setUpdateDate(currentDateTime);

        tbScenarioMasterRepo.save(scMaster);

        logService.LogSave(LogVO.builder()
                .menuAuth(MenuResource.rcpconversationscenariomanage)
                .actionCode(ActionResource.update.getName())
                .registDate(currentDateTime)
                .attribute1(Long.toString(scMaster.getSeq()))
                .remark( scMaster.getScenarioName() + " 시나리오명으로 수정하였습니다.")
                .serviceGroup(dto.getServiceGroup())
                .build());

        return 0;
    }

    @Transactional
    public int scenarioRelease(ConversationScenarioMasterVO dto) throws CustomNotFoundException, CustomBadRequestException {
        String userId = loginService.getUserVo().getUserId();
        LocalDateTime currentDateTime = LocalDateTime.now();
        Integer result = 0;

        if(dto.getSeq() == null || dto.getSeq() == -1) {
            throw new CustomBadRequestException("시나리오 시퀀스가 잘못 되었습니다.");
        }

        TbScenarioMaster scMaster = tbScenarioMasterRepo.findBySeqAndUseYn(dto.getSeq(), "Y")
                .orElseThrow(() -> new CustomNotFoundException("시나리오 정보가 없습니다"));

        // 마지막 시나리오 저장값 가져오기
        List<TbScenarioHistory> scHistory = tbScenarioHistoryRepo.findByMasterSeqAndLastYn(scMaster.getSeq(), "Y");
        if (scHistory.size() == 0 ) {
            throw new CustomNotFoundException("마지막 시나리오 저장 버전을 찾을수 없습니다.");
        }

        TbLongtextManager lastSaveDiagram = tbLongtextManagerRepo.findById(
                        new TbLongtextManagerId(scHistory.get(0).getSeq(),
                                LongTextManagerTableCode.TB_SCENARIO_HISTORY.getCode(),
                                LongTextManagerColumnCode.DIAGRAM.getCode()))
                .orElseThrow(() -> new CustomNotFoundException("마지막 저장 다이어그램 정보를 찾지 못하였습니다."));


        ScenarioConverter scenarioConverter = new ScenarioConverter(lastSaveDiagram.getTextContents());
        Long intentSeq = scenarioConverter.getIntentSeq();
        if (intentSeq == -1L) {
            throw new CustomBadRequestException("인텐트 노드 설정이 잘못되었습니다.");
        }

        // 중복 체크 중복될 경우 Error 처리
        List<TbScenarioRelease> duplicationIntent = tbScenarioReleaseRepo.findByIntentSeqAndReleaseYn(intentSeq, "Y");
        if (duplicationIntent.size() != 0 &&
                (duplicationIntent.size() != 1 || !duplicationIntent.get(0).getMasterSeq().equals(scMaster.getSeq()) )) {

            throw new CustomBadRequestException("중복된 인텐트로 시나리오를 배포할 수 없습니다.");
        }
        
        TbLongtextManager lastSaveScenario = tbLongtextManagerRepo.findById(
                        new TbLongtextManagerId(scHistory.get(0).getSeq(),
                                LongTextManagerTableCode.TB_SCENARIO_HISTORY.getCode(),
                                LongTextManagerColumnCode.SCENARIO.getCode()))
                .orElseThrow(() -> new CustomNotFoundException("마지막 저장 시나리오 정보를 찾지 못하였습니다."));

        // 마지막 저장 시나리오 유효성 검사
        scenarioConverter.validationCheckScenario(lastSaveScenario.getTextContents());

        // Master 릴리즈 정보 있는지 확인
        TbScenarioRelease maxRelease = tbScenarioReleaseRepo.findTopByMasterSeqOrderByVersionDesc(scMaster.getSeq());
        int releaseVersion = 0;
        if (maxRelease == null) {
            releaseVersion = 1;
        } else {
            releaseVersion = maxRelease.getVersion() + 1;
        }

        // 이전 release N 업데이트
        TbScenarioRelease prevRelease = tbScenarioReleaseRepo.findByMasterSeqAndReleaseYn(scMaster.getSeq(), "Y");
        if (prevRelease != null) {
            prevRelease.setReleaseYn("N");
            tbScenarioReleaseRepo.save(prevRelease);
        }

        // Release 테이블 저장
        TbScenarioRelease releaseVo =  TbScenarioRelease.builder()
                .masterSeq(scHistory.get(0).getMasterSeq())
                .releaseYn("Y")
                .version(releaseVersion)
                .registId(userId)
                .registDate(currentDateTime)
                .intentSeq(intentSeq).build();

        tbScenarioReleaseRepo.save(releaseVo);

        // Diagram 입력
        TbLongtextManager releaseDiagramVo = new TbLongtextManager( releaseVo.getSeq(),
                LongTextManagerTableCode.TB_SCENARIO_RELEASE.getCode(),
                LongTextManagerColumnCode.DIAGRAM.getCode(),
                lastSaveDiagram.getTextContents(),
                lastSaveDiagram.getChecksum());
        tbLongtextManagerRepo.save(releaseDiagramVo);
        
        // 콜 시나리오 저장
        TbLongtextManager releaseScenarioVo = new TbLongtextManager( releaseVo.getSeq(),
                LongTextManagerTableCode.TB_SCENARIO_RELEASE.getCode(),
                LongTextManagerColumnCode.SCENARIO.getCode(),
                lastSaveScenario.getTextContents(),
                lastSaveScenario.getChecksum());
        tbLongtextManagerRepo.save(releaseScenarioVo);

        // LastYn 모두 N 업데이트
        for (TbScenarioHistory history: scHistory) {
            history.setLastYn("N");
            tbScenarioHistoryRepo.save(history);
        }
        scMaster.setUpdateId(userId);
        scMaster.setUpdateDate(currentDateTime);
        tbScenarioMasterRepo.save(scMaster);

        // 연관 인텐트에 배포 인텐트가 동일 할 경우 연관인텐트 제거
        List<TbScenarioRelatedIntent> relatedIntents = tbScenarioRelatedIntentRepo.findByScenarioSeqOrderByIntentSeq(scMaster.getSeq());
        for (TbScenarioRelatedIntent intent : relatedIntents) {
            if (intent.getIntentSeq().equals(intentSeq)) {
                tbScenarioRelatedIntentRepo.delete(intent);
                result = 1;     // 배포 인텐트에 연관 인텐트가 있을 경우 return 1;
            }
        }

        logService.LogSave(LogVO.builder()
                .menuAuth(MenuResource.rcpconversationscenariomanage)
                .actionCode(ActionResource.release.getName())
                .registDate(currentDateTime)
                .attribute1(Long.toString(scMaster.getSeq()))           // 시나리오 마스터 seq
                .attribute2(Long.toString(releaseVo.getSeq()))          // 시나리오 release seq
                .remark( scMaster.getScenarioName() + " " + String.format("V%03d", releaseVo.getVersion()) + " 버전을 배포하였습니다.")
                .serviceGroup(dto.getServiceGroup())
                .build());

        return result;
    }

    @Transactional
    public int scenarioRollback(ConversationScenarioMasterVO dto) throws CustomNotFoundException, CustomBadRequestException {
        String userId = loginService.getUserVo().getUserId();
        LocalDateTime currentDateTime = LocalDateTime.now();
        Integer result = 0;

        if(dto.getSeq() == null || dto.getSeq() == -1) {
            throw new CustomBadRequestException("시나리오 시퀀스가 잘못 되었습니다.");
        }

        if(dto.getReleaseSeq() == null || dto.getReleaseSeq() == -1) {
            throw new CustomBadRequestException("배포 시나리오 시퀀스가 잘못 되었습니다.");
        }

        // 시나리오 마스터 검색
        TbScenarioMaster scMaster = tbScenarioMasterRepo.findBySeqAndUseYn(dto.getSeq(), "Y")
                .orElseThrow(() -> new CustomNotFoundException("시나리오 정보가 없습니다"));

        // 시나리오 Release 버전 확인
        TbScenarioRelease prevRelease = tbScenarioReleaseRepo.findByMasterSeqAndReleaseYn(scMaster.getSeq(), "Y");
        if (prevRelease != null) {
            prevRelease.setReleaseYn("N");
            tbScenarioReleaseRepo.save(prevRelease);
        }

        // 시나리오 롤 백 버전 확인
        TbScenarioRelease rollbackRelease = tbScenarioReleaseRepo.findById(dto.getReleaseSeq())
                .orElseThrow(() -> new CustomNotFoundException("롤백 버전 정보가 없습니다."));

        rollbackRelease.setReleaseYn("Y");
        rollbackRelease.setRegistDate(currentDateTime);
        rollbackRelease.setRegistId(userId);
        tbScenarioReleaseRepo.save(rollbackRelease);

        // 시나리오 저장 히스토리 버전 N으로 수정
        List<TbScenarioHistory> scHistory = tbScenarioHistoryRepo.findByMasterSeqAndLastYn(dto.getSeq(), "Y");
        for (TbScenarioHistory history: scHistory) {
            history.setLastYn("N");
            tbScenarioHistoryRepo.save(history);
        }

        scMaster.setUpdateId(userId);
        scMaster.setUpdateDate(currentDateTime);
        tbScenarioMasterRepo.save(scMaster);

        // 연관 인텐트에 롤백 인텐트가 동일 할 경우 연관인텐트 제거
        List<TbScenarioRelatedIntent> relatedIntents = tbScenarioRelatedIntentRepo.findByScenarioSeqOrderByIntentSeq(scMaster.getSeq());
        for (TbScenarioRelatedIntent intent : relatedIntents) {
            if (intent.getIntentSeq().equals(rollbackRelease.getIntentSeq())) {
                tbScenarioRelatedIntentRepo.delete(intent);
                result = 1;     // 롤백 인텐트에 연관인텐트가 있을경우 return 1
            }
        }

        logService.LogSave(LogVO.builder()
                .menuAuth(MenuResource.rcpconversationscenariomanage)
                .actionCode(ActionResource.rollback.getName())
                .registDate(currentDateTime)
                .attribute1(Long.toString(scMaster.getSeq()))           // 시나리오 마스터 seq
                .attribute2(Long.toString(rollbackRelease.getSeq()))          // 시나리오 release seq
                .remark( scMaster.getScenarioName() + " " +String.format("V%03d", rollbackRelease.getVersion()) + " 버전으로 롤백 하였습니다.")
                .serviceGroup(dto.getServiceGroup())
                .build());

        return result;
    }

    @Transactional
    public int scenarioReload(ConversationScenarioMasterVO dto) throws CustomNotFoundException, CustomBadRequestException {
        String userId = loginService.getUserVo().getUserId();
        LocalDateTime currentDateTime = LocalDateTime.now();

        if(dto.getSeq() == null || dto.getSeq() == -1) {
            throw new CustomBadRequestException("시나리오 시퀀스가 잘못 되었습니다.");
        }

        if(dto.getReleaseSeq() == null || dto.getReleaseSeq() == -1) {
            throw new CustomBadRequestException("배포 시나리오 시퀀스가 잘못 되었습니다.");
        }

        // 시나리오 마스터 검색
        TbScenarioMaster scMaster = tbScenarioMasterRepo.findBySeqAndUseYn(dto.getSeq(), "Y")
                .orElseThrow(() -> new CustomNotFoundException("시나리오 정보가 없습니다"));
        
        // 시나리오 마지막 저장 있는지 확인 후 N 변경
        List<TbScenarioHistory> scHistory = tbScenarioHistoryRepo.findByMasterSeqAndLastYn(scMaster.getSeq(), "Y");
        for (TbScenarioHistory history: scHistory) {
            history.setLastYn("N");
            tbScenarioHistoryRepo.save(history);
        }
        // release 버전 Longtext 읽어오기
        TbScenarioRelease reloadRelease = tbScenarioReleaseRepo.findById(dto.getReleaseSeq())
                .orElseThrow(() -> new CustomNotFoundException("불러올 버전 정보가 없습니다."));

        // Longtext 마지막 저장 버전 release 버전으로 읽어온 시나리오 불러오기
        TbLongtextManager loadDiagram = tbLongtextManagerRepo.findById(
                new TbLongtextManagerId( reloadRelease.getSeq(),
                        LongTextManagerTableCode.TB_SCENARIO_RELEASE.getCode(),
                        LongTextManagerColumnCode.DIAGRAM.getCode()))
                .orElseThrow(() -> new CustomNotFoundException("불러올 다이어그램 정보를 찾지 못하였습니다."));

        TbLongtextManager loadScenario = tbLongtextManagerRepo.findById(
                        new TbLongtextManagerId( reloadRelease.getSeq(),
                                LongTextManagerTableCode.TB_SCENARIO_RELEASE.getCode(),
                                LongTextManagerColumnCode.SCENARIO.getCode()))
                .orElseThrow(() -> new CustomNotFoundException("불러올 시나리오 정보를 찾지 못하였습니다."));


        // 시나리오 관리 불러오기 버전 저장
        TbScenarioHistory history = new TbScenarioHistory(scMaster.getSeq(), "Y", userId, currentDateTime);
        tbScenarioHistoryRepo.save(history);

        // 다이어그램 저장
        tbLongtextManagerRepo.save(new TbLongtextManager(
                history.getSeq(),
                LongTextManagerTableCode.TB_SCENARIO_HISTORY.getCode(),
                LongTextManagerColumnCode.DIAGRAM.getCode(),
                loadDiagram.getTextContents(),
                loadDiagram.getChecksum()));

        // Call 시나리오 저장
        tbLongtextManagerRepo.save(new TbLongtextManager(
                history.getSeq(),
                LongTextManagerTableCode.TB_SCENARIO_HISTORY.getCode(),
                LongTextManagerColumnCode.SCENARIO.getCode(),
                loadScenario.getTextContents(),
                loadScenario.getChecksum()));

        scMaster.setUpdateId(userId);
        scMaster.setUpdateDate(currentDateTime);
        tbScenarioMasterRepo.save(scMaster);

        logService.LogSave(LogVO.builder()
                .menuAuth(MenuResource.rcpconversationscenariomanage)
                .actionCode(ActionResource.update.getName())
                .registDate(currentDateTime)
                .attribute1(Long.toString(scMaster.getSeq()))           // 시나리오 마스터 seq
                .attribute2(Long.toString(history.getSeq()))            // 시나리오 history seq
                .remark( scMaster.getScenarioName() + " " + String.format("V%03d", reloadRelease.getVersion()) + " 버전을 불러오기 하였습니다.")
                .serviceGroup(dto.getServiceGroup())
                .build());

        return 0;
    }

    @Transactional
    public int scenarioDelete(ConversationScenarioMasterVO dto) throws CustomNotFoundException, CustomBadRequestException {
        String userId = loginService.getUserVo().getUserId();
        LocalDateTime currentDateTime = LocalDateTime.now();

        if (dto.getSeq() == null || dto.getSeq() == -1) {
            throw new CustomBadRequestException("시나리오 시퀀스가 잘못 되었습니다.");
        }

        // 시나리오 마스터 검색
        TbScenarioMaster scMaster = tbScenarioMasterRepo.findBySeqAndUseYn(dto.getSeq(), "Y")
                .orElseThrow(() -> new CustomNotFoundException("시나리오 정보가 없습니다"));

        scMaster.setUseYn("N");
        scMaster.setUpdateId(userId);
        scMaster.setUpdateDate(currentDateTime);
        tbScenarioMasterRepo.save(scMaster);

        // 시나리오 릴리즈 Y -> N
        TbScenarioRelease scRelease = tbScenarioReleaseRepo.findByMasterSeqAndReleaseYn(scMaster.getSeq(), "Y");
        if (scRelease != null ) {
            scRelease.setReleaseYn("N");
            tbScenarioReleaseRepo.save(scRelease);
        }

        logService.LogSave(LogVO.builder()
                .menuAuth(MenuResource.rcpconversationscenariomanage)
                .actionCode(ActionResource.delete.getName())
                .registDate(currentDateTime)
                .attribute1(Long.toString(scMaster.getSeq()))           // 시나리오 마스터 seq
                .remark( scMaster.getScenarioName() + "를 삭제하였습니다.")
                .serviceGroup(dto.getServiceGroup())
                .build());

        return 0;
    }

    public ConversationScenarioManagerInfoRes scenarioManagerInfo(Long scenarioSeq) throws CustomNotFoundException {
        ConversationScenarioManagerInfoRes result = new ConversationScenarioManagerInfoRes();

        TbScenarioMaster scMaster = tbScenarioMasterRepo.findBySeqAndUseYn(scenarioSeq, "Y").
                orElseThrow(() -> new CustomNotFoundException("시나리오 정보가 없습니다"));

        result.setSeq(scMaster.getSeq());
        result.setScenarioName(scMaster.getScenarioName());

        TbScenarioRelease scRelease = tbScenarioReleaseRepo.findByMasterSeqAndReleaseYn(scenarioSeq, "Y");
        if (scRelease != null) {
            result.setReleaseVersion(scRelease.getVersion());
        } else {
            result.setReleaseVersion(0);
        }

        List<TbScenarioHistory> scHistory = tbScenarioHistoryRepo.findByMasterSeqAndLastYn(scenarioSeq, "Y");
        String userId;
        if (scHistory.size() == 0) {
            if (scRelease == null) {
                result.setModifyType(0);    // 생성
            } else {
                result.setModifyType(1);    // 배포중
            }
            result.setLastSaveDateTime(scMaster.getUpdateDate());
            userId = scMaster.getUpdateId();
        } else {
            result.setModifyType(2);    // 수정중
            result.setLastSaveDateTime(scHistory.get(0).getRegistDate());
            userId = scHistory.get(0).getRegistId();
        }

        TbBotUser updateUser = tbBotUserRepo.findByUserId(userId)
                .orElseThrow(() -> new CustomNotFoundException("사용자 정보가 없습니다"));
        result.setUpdateName(updateUser.getUserName());

        return result;
    }

    public ConversationScenarioDiagramRes selectScenarioDiagram(ConversationScenarioMasterVO dto) throws CustomNotFoundException, CustomBadRequestException {
        if (dto.getSeq() == null || dto.getSeq() == -1) {
            throw new CustomBadRequestException("시나리오 시퀀스 번호가 비었습니다.");
        }

        Long masterSeq = dto.getSeq();
        ConversationScenarioDiagramRes result = new ConversationScenarioDiagramRes();
        TbScenarioMaster scMaster = tbScenarioMasterRepo.findBySeqAndUseYn(masterSeq, "Y").
                orElseThrow(() -> new CustomNotFoundException("시나리오 정보가 없습니다"));

        result.setMasterSeq(scMaster.getSeq());
        result.setScenarioName(scMaster.getScenarioName());

        List<TbScenarioHistory> scHistory = tbScenarioHistoryRepo.findByMasterSeqAndLastYn(masterSeq, "Y");
        // 마지막 저장 다이어그램이 있는지 확인
        if (scHistory.size() != 0) {
            TbLongtextManager diagram = tbLongtextManagerRepo.findById(
                    new TbLongtextManagerId(scHistory.get(0).getSeq(),
                            LongTextManagerTableCode.TB_SCENARIO_HISTORY.getCode(),
                            LongTextManagerColumnCode.DIAGRAM.getCode()))
                                .orElseThrow(() -> new CustomNotFoundException("시나리오 다이어그램 정보를 찾지 못하였습니다."));

            result.setTextContents(diagram.getTextContents());
            result.setChecksum(diagram.getChecksum());
        } else {    // 배포중인 다이어그램 확인
            TbScenarioRelease scRelease = tbScenarioReleaseRepo.findByMasterSeqAndReleaseYn(masterSeq, "Y");
            if(scRelease == null) { // 시나리오 생성 후 처음으로 편집하는 경우
                result.setTextContents("");
            } else {
                TbLongtextManager diagram = tbLongtextManagerRepo.findById(
                        new TbLongtextManagerId(scRelease.getSeq(),
                                LongTextManagerTableCode.TB_SCENARIO_RELEASE.getCode(),
                                LongTextManagerColumnCode.DIAGRAM.getCode()))
                        .orElseThrow(() -> new CustomNotFoundException("시나리오 다이어그램 정보를 찾지 못하였습니다."));

                result.setTextContents(diagram.getTextContents());
                result.setChecksum(diagram.getChecksum());
            }
        }

        return result;
    }

    @Transactional
    public int saveScenarioDiagram(ConversationScenarioDiagramVO dto) throws CustomBadRequestException, CustomNotFoundException {
        String userId = loginService.getUserVo().getUserId();
        LocalDateTime currentDateTime = LocalDateTime.now();

        if (dto.getMasterSeq() == null || dto.getMasterSeq() == -1) {
            throw new CustomBadRequestException("scenario master seq 값이 없습니다");
        }

        TbScenarioMaster scMaster = tbScenarioMasterRepo.findBySeqAndUseYn(dto.getMasterSeq(), "Y").
                orElseThrow(() -> new CustomNotFoundException("시나리오 정보가 없습니다"));

        Long masterSeq = dto.getMasterSeq();
        List<TbScenarioHistory> lastSave = tbScenarioHistoryRepo.findByMasterSeqAndLastYn(masterSeq, "Y");

        // 시나리오 변환기 생성
        ScenarioConverter scenarioConverter = new ScenarioConverter(dto.getTextContents());
        try {
            scenarioConverter.createDiagramChecksum();
        } catch (NoSuchAlgorithmException e) {
            throw new CustomBadRequestException("시나리오 다이어그램 해시값 생성에 실패하였습니다.");
        }

        if (lastSave.size() != 0) { // 이전 기록이 있는 경우 저장
            // 체크썸 확인, last N 수정 후 신규 저장
            TbLongtextManager lastDiagram =tbLongtextManagerRepo.findById(
                            new TbLongtextManagerId(lastSave.get(0).getSeq(),
                                    LongTextManagerTableCode.TB_SCENARIO_HISTORY.getCode(),
                                    LongTextManagerColumnCode.DIAGRAM.getCode()))
                    .orElseThrow(() -> new CustomNotFoundException("시나리오 다이어그램 정보를 찾지 못하였습니다."));

            if (lastDiagram.getChecksum().equals(scenarioConverter.getDiagramChecksum())) {   // 변경된 값이 없음
                return 1;
            }

            // 이전 히스토리 없데이트
            for (TbScenarioHistory element: lastSave) {
                element.setLastYn("N");
                tbScenarioHistoryRepo.save(element);
            }
        }

        scenarioConverter.convertDiagramToCallScenario();
        try {
            scenarioConverter.createScenarioChecksum();
        } catch (NoSuchAlgorithmException e) {
            throw new CustomBadRequestException("콜 시나리오 해시값 생성에 실패하였습니다.");
        }

        TbScenarioHistory history = new TbScenarioHistory(masterSeq, "Y", userId, currentDateTime);
        tbScenarioHistoryRepo.save(history);

        // Diagram 저장
        tbLongtextManagerRepo.save(new TbLongtextManager(
                history.getSeq(),
                LongTextManagerTableCode.TB_SCENARIO_HISTORY.getCode(),
                LongTextManagerColumnCode.DIAGRAM.getCode(),
                scenarioConverter.getDiagramString(),
                scenarioConverter.getDiagramChecksum()));

        // Call Scenario 저장
        tbLongtextManagerRepo.save(new TbLongtextManager(
                history.getSeq(),
                LongTextManagerTableCode.TB_SCENARIO_HISTORY.getCode(),
                LongTextManagerColumnCode.SCENARIO.getCode(),
                scenarioConverter.getCallScenarioString(),
                scenarioConverter.getCallScenarioChecksum()));

        scMaster.setUpdateId(userId);
        scMaster.setUpdateDate(currentDateTime);
        tbScenarioMasterRepo.save(scMaster);

        logService.LogSave(LogVO.builder()
                .menuAuth(MenuResource.rcpconversationscenariomanage)
                .actionCode(ActionResource.update.getName())
                .registDate(currentDateTime)
                .attribute1(Long.toString(scMaster.getSeq()))           // 시나리오 마스터 seq
                .attribute2(Long.toString(history.getSeq()))            // 사나리오 history seq
                .remark( scMaster.getScenarioName() + " 시나리오를 저장하였습니다.")
                .serviceGroup(dto.getServiceGroup())
                .build());

        return 0;
    }

    public LongtextManagerVO selectTestContents( String scenarioMaster) throws CustomBadRequestException, CustomNotFoundException {
        Long masterSeq = Long.valueOf(scenarioMaster);
        if( masterSeq == null ) {
            throw new CustomBadRequestException("시나리오 시퀀스가 잘못되었습니다.");
        }

        tbScenarioMasterRepo.findBySeqAndUseYn(masterSeq, "Y").
                orElseThrow(() -> new CustomNotFoundException("시나리오 정보가 없습니다"));

        TbLongtextManager scenario = null;
        List<TbScenarioHistory> scHistory = tbScenarioHistoryRepo.findByMasterSeqAndLastYn(masterSeq, "Y");
        if (scHistory.size() != 0) {
            scenario =  tbLongtextManagerRepo.findById(new TbLongtextManagerId( scHistory.get(0).getSeq(),
                            LongTextManagerTableCode.TB_SCENARIO_HISTORY.getCode(),
                            LongTextManagerColumnCode.SCENARIO.getCode()))
                    .orElseThrow(() -> new CustomNotFoundException("시나리오 컨텐츠 정보를 찾지 못하였습니다."));

            // scenario 요효성 검사
            ScenarioConverter scenarioConverter = new ScenarioConverter();
            scenarioConverter.validationCheckScenario(scenario.getTextContents());

        } else {
            TbScenarioRelease scRelease = tbScenarioReleaseRepo.findByMasterSeqAndReleaseYn(masterSeq, "Y");
            if(scRelease == null) { // 시나리오 생성 후 처음으로 편집하는 경우
                throw new CustomBadRequestException("테스트를 진행할 시나리오 정보가 없습니다.");
            } else {
                scenario = tbLongtextManagerRepo.findById(
                                new TbLongtextManagerId(scRelease.getSeq(),
                                        LongTextManagerTableCode.TB_SCENARIO_RELEASE.getCode(),
                                        LongTextManagerColumnCode.SCENARIO.getCode()))
                        .orElseThrow(() -> new CustomNotFoundException("시나리오 컨텐츠 정보를 찾지 못하였습니다."));
            }
        }

        return new LongtextManagerVO().ofTbLongtextManager(scenario);
    }

    public QueryResults<ConversationScenarioWorkHistoryRes> scenarioHistoryLog(ConversationScenarioMasterVO dto) throws CustomBadRequestException {
        if (dto.getSeq() == null || dto.getSeq() == -1) {
            throw new CustomBadRequestException("scenario seq 값이 없습니다");
        }

        QueryResults<ConversationScenarioWorkHistoryRes> logHistory = tbIcsLogRepo.scenarioWorkHistory(
                MenuResource.rcpconversationscenariomanage.getName(),
                Long.toString(dto.getSeq()));

        // return new DslDataTableRes<ConversationScenarioWorkHistoryRes>(logHistory);
        return logHistory;
    }

    public QueryResults<ConversationScenarioRelatedIntentVO> scenarioRelatedIntent( ConversationScenarioMasterVO dto) throws CustomBadRequestException {
        if (dto.getSeq() == null || dto.getSeq() == -1) {
            throw new CustomBadRequestException("scenario seq 값이 없습니다");
        }

        return tbScenarioRelatedIntentRepo.findRelatedIntentList(dto);
    }

    @Transactional
    public int scenarioRelatedIntentSave( ConversationScenarioRelatedIntentSaveReq dto) throws CustomBadRequestException, CustomNotFoundException {
        String userId = loginService.getUserVo().getUserId();
        LocalDateTime currentDateTime = LocalDateTime.now();
        Integer result = 0;

        if (dto.getScenarioSeq() == null || dto.getScenarioSeq() == -1) {
            throw new CustomBadRequestException("scenario seq 값이 없습니다");
        }

        List<Long> saveIntents = dto.getCheckIntents();
        List<TbScenarioRelatedIntent> prevIntent =  tbScenarioRelatedIntentRepo.findByScenarioSeqOrderByIntentSeq(dto.getScenarioSeq());

        // 배포된 자기 자신의 Intent는 연관 인텐트로 저장 X
        TbScenarioRelease scRelease = tbScenarioReleaseRepo.findByMasterSeqAndReleaseYn(dto.getScenarioSeq(), "Y");
        if(scRelease != null) {
            if (saveIntents.contains(Long.valueOf(scRelease.getIntentSeq()))) {
                saveIntents.remove(Long.valueOf(scRelease.getIntentSeq()));
                result = 1;     // 자기자신 연관 인텐트
            }
        }

        Collections.sort(saveIntents);

        int i =0, j=0;
        while ( i < prevIntent.size() && j < saveIntents.size() ) {
            if (prevIntent.get(i).getIntentSeq() == saveIntents.get(j) ) {
                i++; j++;
            } else if (prevIntent.get(i).getIntentSeq() > saveIntents.get(j) ) {
                tbScenarioRelatedIntentRepo.save(new TbScenarioRelatedIntent(dto.getScenarioSeq(), saveIntents.get(j)));
                j++;
            } else {
                tbScenarioRelatedIntentRepo.delete(new TbScenarioRelatedIntent(dto.getScenarioSeq(), prevIntent.get(i).getIntentSeq()));
                i++;
            }
        }

        while (i < prevIntent.size()) {
            tbScenarioRelatedIntentRepo.delete(new TbScenarioRelatedIntent(dto.getScenarioSeq(), prevIntent.get(i).getIntentSeq()));
            i++;
        }

        while (j < saveIntents.size()) {
            tbScenarioRelatedIntentRepo.save(new TbScenarioRelatedIntent(dto.getScenarioSeq(),saveIntents.get(j)));
            j++;
        }

        TbScenarioMaster scMaster = tbScenarioMasterRepo.findBySeqAndUseYn(dto.getScenarioSeq(), "Y").
                orElseThrow(() -> new CustomNotFoundException("시나리오 정보가 없습니다"));

        scMaster.setUpdateId(userId);
        scMaster.setUpdateDate(currentDateTime);
        tbScenarioMasterRepo.save(scMaster);

        logService.LogSave(LogVO.builder()
                .menuAuth(MenuResource.rcpconversationscenariomanage)
                .actionCode(ActionResource.update.getName())
                .registDate(currentDateTime)
                .attribute1(Long.toString(dto.getScenarioSeq()))           // 시나리오 마스터 seq
                .remark( scMaster.getScenarioName() + " 시나리오를 저장하였습니다.")
                .serviceGroup(dto.getServiceGroup())
                .build());

        return result;
    }
}
