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

import com.google.gson.Gson;
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.ExcelService;
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.rcp.conversation.dto.IntentRequestDTO;
import com.icomsys.main_vm.biz.rcp.conversation.dto.TbIntentExampleDto;
import com.icomsys.main_vm.biz.rcp.conversation.nlp.wisenut.service.NlpWisenutIntentService;
import com.icomsys.main_vm.biz.rcp.conversation.nlp.wisenut.vo.NlpWisenutResult;
import com.icomsys.main_vm.biz.rcp.conversation.nlp.wisenut.vo.SentenceListVO;
import com.icomsys.main_vm.biz.rcp.conversation.vo.TbBotCommonCodeVo;
import com.icomsys.main_vm.biz.rcp.conversation.vo.intent.ExcelIntentListVo;
import com.icomsys.main_vm.biz.rcp.conversation.vo.intent.ExcelIntentListVoEx;
import com.icomsys.main_vm.biz.rcp.conversation.vo.intent.IntentSearchVo;
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.repo.system.TbBotCommonCodeRepo;
import com.icomsys.main_vm.db.jpa.repo.system.TbIntentExampleRepo;
import com.icomsys.main_vm.db.jpa.repo.system.TbIntentMasterRepo;
import com.icomsys.main_vm.db.jpa.repo.system.TbServiceGrouopRepo;
import com.icomsys.main_vm.db.mybatis.alias.LoginVO;
import com.icomsys.main_vm.db.mybatis.mapper.IntentManageMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.CellType;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.json.MappingJackson2JsonView;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;


@Service
@Slf4j
@RequiredArgsConstructor
public class IntentManageService {

    private final NlpWisenutIntentService nlpWisenutIntentService;
    private final HttpServletRequest request;
    private final MappingJackson2JsonView ajaxMainView;
    private final TbIntentMasterRepo tbIntentMasterRepo;
    private final TbBotCommonCodeRepo tbBotCommonCodeRepo;
    private final TbIntentExampleRepo tbIntentExampleRepo;
    private final IntentManageMapper intentManageMapper;
    private final LogService logService;
    private final TbServiceGrouopRepo tbServiceGrouopRepo;
    private final ExcelService excelService;

    public ModelAndView searchIntent(IntentSearchVo dto) throws CustomNotFoundException {
        ModelAndView mv = new ModelAndView(ajaxMainView);
        dto.setServiceGroupUpper(tbServiceGrouopRepo.findByServiceGroup(dto.getServiceGroup()).orElseThrow(() -> new CustomNotFoundException()).getServiceGroupUpper());
        mv.addObject("list", tbIntentMasterRepo.selectIntentList(dto));
        return mv;
    }

    public ModelAndView searchIntentCategoryList(Map<String, String> dataMap) throws CustomNotFoundException {
        ModelAndView mv = new ModelAndView(ajaxMainView);
        HashMap eMap = new HashMap();
        eMap.putAll(dataMap);

//        String oprMngCode = eMap.get("custCode") + "_RCP_" + eMap.get("serviceGroup");
        String oprMngCode = eMap.get("custCode") + "_RCP_" + tbServiceGrouopRepo
                .findByServiceGroup(eMap.get("serviceGroup").toString())
                .orElseThrow(() -> new CustomNotFoundException()).getServiceGroupUpper();
        log.info("oprMngCode -> {}", oprMngCode);
        List<TbBotCommonCodeVo> cate = tbBotCommonCodeRepo.findByCustCodeAndMajorCodeAndOprMngCodeOrderByCodeNameAsc
                        (String.valueOf(eMap.get("custCode")), "INTENT_GROUP", oprMngCode)
                .stream().map(e -> e.toDecs())
                .collect(Collectors.toList());

        mv.addObject("categoryList", cate);
        return mv;
    }

    public ModelAndView selectIntentExampleList(Map<String, String> dataMap) {

        ModelAndView mv = new ModelAndView(ajaxMainView);
        List<TbIntentExampleDto> list = tbIntentExampleRepo.findByIntentSeqAndUseYn(
                        Long.parseLong(dataMap.get("intentSeq").toString()), "Y")
                .stream().map(e -> e.toTbIntentExampleDto())
                .collect(Collectors.toList());

        mv.addObject("list", list);
        return mv;
    }

    public int selectIntentCnt(HashMap eMap) {
        return intentManageMapper.selectIntentCnt(eMap);
    }

    @Transactional
    public ModelAndView updateIntent(IntentRequestDTO dto) {
        ModelAndView mv = new ModelAndView(ajaxMainView);
        boolean result = true;
        String message = "저장되었습니다.";

        try {
            LoginVO resultVO = (LoginVO) request.getSession().getAttribute("LoginVO");
            dto.setRegistId(resultVO.getUserId());
            dto.setUpdateId(resultVO.getUserId());
            //tbIntentMasterRepo.updateIntentMaster(dto);
            HashMap eMap = dto.toMap();

            log.info("eMap -> {}", new Gson().toJson(eMap));
            // NLP API
            NlpWisenutResult nlpWisenutResult = nlpWisenutIntentService.intentUpdateApi(eMap);

            //String oprMngCode = eMap.get("custCode") + "_RCP_" + eMap.get("serviceGroup");
            if (nlpWisenutResult.getResult()) {
                this.updateIntentMaster(eMap);
                this.workLogSave(MenuResource.rcpconversationintentmanage, ActionResource.update, String.valueOf(eMap.get("serviceGroup")), "인텐트 수정");
            } else {
                log.error(nlpWisenutResult.getReturnMsg() + " : " + nlpWisenutResult.getReturnCode());
                throw new DuplicateKeyException(nlpWisenutResult.getReturnMsg());
            }
        } catch (DuplicateKeyException e) {
            result = false;
            message = e.getMessage();
        } catch (Exception e) {
            result = false;
            message = "처리도중 오류가 발생했습니다.";
            log.error("error", e);
        }
        mv.addObject("result", result);
        mv.addObject("message", message);
        return mv;
    }

    public void insertIntentMaster(HashMap eMap) {
        int cnt = this.selectIntentCnt(eMap);
        if (cnt > 0) {
            throw new DuplicateKeyException("중복된 인텐트명 입니다.");
        }

        // NLP API
        String oprMngCode = eMap.get("custCode") + "_RCP_" + eMap.get("serviceGroup");
        eMap.put("oprMngCode", oprMngCode);
        NlpWisenutResult nlpWisenutResult = nlpWisenutIntentService.intentInsertApi(eMap);
        if (nlpWisenutResult.getResult()) {
            eMap.put("mappingId", nlpWisenutResult.getMappingId());
            intentManageMapper.insertIntentMaster(eMap);
            this.workLogSave(MenuResource.rcpconversationintentmanage, ActionResource.insert, String.valueOf(eMap.get("serviceGroup")), "인텐트 등록");
        } else {
            log.error(nlpWisenutResult.getReturnCode() + " : " + nlpWisenutResult.getReturnMsg());
            throw new DuplicateKeyException(nlpWisenutResult.getReturnMsg());
        }
    }

    public void deleteIntentExample_one(HashMap eMap) {
        // NLP API
        NlpWisenutResult nlpWisenutResult = nlpWisenutIntentService.intentSentenceDeleteApi(eMap);
        if (nlpWisenutResult.getResult()) {
//            TbIntentExample tbIntentExample = tbIntentExampleRepo.findBySeq(eMap.get("seq")).get();
//            tbIntentExampleRepo.delete(tbIntentExample);
            intentManageMapper.deleteIntentExample_one(eMap);
            //this.workLogSave(MenuResource.rcpconversationintentmanage, ActionResource.delete, String.valueOf(eMap.get("oprMngCode")));
            logService.LogSave(LogVO.builder()
                    .menuAuth(MenuResource.valueOf(String.valueOf(eMap.get("menuVal"))))
                    .actionCode(String.valueOf((ActionResource.delete)))
                    .serviceGroup(String.valueOf(eMap.get("serviceGroup")))
                    .remark("예문 삭제")
                    .build());
        } else {
            log.error(nlpWisenutResult.getReturnMsg() + " : " + nlpWisenutResult.getReturnCode());
            throw new DuplicateKeyException(nlpWisenutResult.getReturnMsg());
        }
    }

    public void insertIntentExample(HashMap eMap) {
        // NLP API
        NlpWisenutResult nlpWisenutResult = nlpWisenutIntentService.intentSentenceInsertApi(eMap);
        if (nlpWisenutResult.getResult()) {
            eMap.put("mappingId", nlpWisenutResult.getMappingId());
            intentManageMapper.insertIntentExample(eMap);
            logService.LogSave(LogVO.builder()
                    .menuAuth(MenuResource.valueOf(String.valueOf(eMap.get("menuVal"))))
                    .actionCode(String.valueOf((ActionResource.insert)))
                    .serviceGroup(String.valueOf(eMap.get("serviceGroup")))
                    .remark("예문 등록")
                    .build());
        } else {
            log.error(nlpWisenutResult.getReturnCode() + " : " + nlpWisenutResult.getReturnMsg());
            throw new DuplicateKeyException(nlpWisenutResult.getReturnMsg());
        }
    }

    public void updateIntentMaster(HashMap eMap) {
        intentManageMapper.updateIntentMaster(eMap);
    }

    public void updateDeleteIntentExample(HashMap eMap) {
        intentManageMapper.updateDeleteIntentExample(eMap);
    }

    public void deleteIntent(HashMap eMap) {
        eMap.put("useYn", "N");
        // NLP API
        NlpWisenutResult nlpWisenutResult = nlpWisenutIntentService.intentDeleteApi(eMap);
        if (nlpWisenutResult.getResult()) {
            this.updateIntentMaster(eMap);
            this.updateDeleteIntentExample(eMap);
            this.workLogSave(MenuResource.rcpconversationintentmanage, ActionResource.delete, String.valueOf(eMap.get("servicegroup")), "인텐트 삭제");
        } else {
            log.error(nlpWisenutResult.getReturnMsg() + " : " + nlpWisenutResult.getReturnCode());
            throw new DuplicateKeyException(nlpWisenutResult.getReturnMsg());
        }
    }

    public void insertIntent(HashMap eMap) throws DuplicateKeyException {

        this.insertIntentMaster(eMap);
        this.insertIntentExample(eMap);
    }

    public String insertIntentExcel(MultipartHttpServletRequest request, HashMap eMap) throws Exception {
        List<HashMap> list = new ArrayList<HashMap>();
        List<HashMap> exList = new ArrayList<HashMap>();
        MultipartFile file = request.getFile("upFile");
        HSSFWorkbook wb = new HSSFWorkbook(file.getInputStream());
        HSSFSheet sheet = wb.getSheetAt(0);

        // 시트 확인
        String sheetName = sheet.getSheetName();
        if (!StringUtils.equals("data", sheetName)) {
            throw new CustomRuntimeException("엑셀 양식이 잘못되었습니다.");
        }


        String oprMngCode = (String) eMap.get("oprMngCode");
        String custCode = (String) eMap.get("custCode");
        String registId = (String) eMap.get("registId");
        String updateId = (String) eMap.get("updateId");
        String serviceGroup = (String) eMap.get("serviceGroup");

        /**
         * - 인텐트 1 : N 인텐트 예문 관계라 똑같은 목록을 두번 반복(중첩)해서 리스트를 읽음.
         * - 첫번째 반복은 인텐트 명, 인텐트 분류 데이터를 얻기 위함.
         * - 두번째 반복은 해당 인텐트의 예문 목록을 얻기 위함.
         *
         * 1. 인텐트가 중복이 아닌 경우
         * - 인텐트 명, 인텐트 분류, 인텐트 예문 목록 저장
         * - 해당 인텐트의 예문 목록을 두번째 반복문으로 구한 후 목록 저장
         *
         * 2. 인텐트가 중복인 경우
         * - 로직 없음.
         * - 기존 인텐트와의 중복성 검사 등록 시 처리.
         *
         * 현재 최종 데이터의 형태
         * [
         *    {
         * 		intentName: "인텐트 명",
         * 		category: "인텐트 분류",
         * 		exampleList: [
         *            {exampleText: "예문1"}, {exampleText: "예문2"}
         * 		]
         *    },
         *  {
         * 		intentName: "인텐트 명2",
         * 		category: "인텐트 분류2",
         * 		exampleList: [
         *            {exampleText: "예문1"}, {exampleText: "예문2"}
         * 		]
         *    },
         * ]
         *
         * 데이터 가공 후 각각의 Map으로 인텐트 저장.
         * 엑셀 입력 시 분류는CODE_DESC 값을 입력, 저장시엔 MINOR_CODE 값을 입력해야 하기에 category code를 가져오는 코드 추가.
         */

        String beforeIntentName = new String();
        for (int i = 1; i < sheet.getPhysicalNumberOfRows(); i++) {
            HSSFRow row = sheet.getRow(i);
            if (row == null) continue;

            // 인서트용 데이터
            HashMap tmp = new HashMap();

            List<IntentRequestDTO> exampleList = new ArrayList<>();


            String cellVal;
            // 인텐트 명
            if (row.getCell(0) != null) {
                row.getCell(0).setCellType(CellType.STRING);
                cellVal = row.getCell(0).getStringCellValue();

                if (!StringUtils.equals(beforeIntentName, cellVal)) {
                    tmp.put("intentName", cellVal);

                    // 고정데이터
                    tmp.put("oprMngCode", oprMngCode);
                    tmp.put("custCode", custCode);
                    tmp.put("registId", registId);
                    tmp.put("updateId", updateId);
                    tmp.put("serviceGroup", serviceGroup);

                    // 예문 목록 추출
                    for (int j = 1; j < sheet.getPhysicalNumberOfRows(); j++) {
                        HSSFRow exRow = sheet.getRow(j);
                        String exCellVal;

                        // 해당되는 인텐트의 예문을 찾기 위함.
                        if (exRow.getCell(0) == null || row.getCell(0).getStringCellValue().equals("")) {
                            continue;
                        }
                        exRow.getCell(0).setCellType(CellType.STRING);
                        String exIntentName = exRow.getCell(0).getStringCellValue();
                        if (StringUtils.equals(cellVal, exIntentName))
                            // 예문
                            if (exRow.getCell(1) != null) {
                                exRow.getCell(1).setCellType(CellType.STRING);
                                exCellVal = exRow.getCell(1).getStringCellValue();

                                Iterator<String> iter = Arrays.stream(exCellVal.split(",")).iterator();

                                while (iter.hasNext()) {
                                    IntentRequestDTO dto = new IntentRequestDTO();
                                    dto.setExampleText(iter.next());
                                    exampleList.add(dto);
                                }

//                                IntentRequestDTO dto = new IntentRequestDTO();
//                                dto.setExampleText(exCellVal);
//                                exampleList.add(dto);

//                                IntentRequestDTO dto = new IntentRequestDTO();
//                                dto.setExampleText(exCellVal);
//                                exampleList.add(dto);


//                                XSSFCell cell = row.getCell(1);
//                                String exampleList = cell.getStringCellValue();
//
//                                eMap.put("exampleList", exampleList.replaceAll("\"", ""));
//                                String[] list = exampleList.replaceAll(", ", ",").split(",");
                           }
                    }

                    if (exampleList.size() > 200) {
                        throw new CustomRuntimeException("예문 제한을 초과했습니다.");
                    }

                    // 예문 목록 저장
                    tmp.put("exampleList", exampleList);

                    // 인텐트 명 구분용
                    beforeIntentName = cellVal;

                    log.info(String.valueOf(row.getCell(2)));
                    // 분류 저장
                    if (row.getCell(2) == null || row.getCell(2).getStringCellValue().equals("")) {
                        continue;
                    }

                    row.getCell(2).setCellType(CellType.STRING);
                    cellVal = row.getCell(2).getStringCellValue();
                    tmp.put("category", cellVal);

                    list.add(tmp);
                }
            }
        }

        if (list.size() > 50) {
            throw new CustomRuntimeException("인텐트 제한을 초과했습니다.");
        }

        // 인텐트 등록
        for (HashMap em : list) {
            HashMap categoryMap = intentManageMapper.selectIntentCategoryCode(em);
            if (categoryMap == null || StringUtils.isBlank((String) categoryMap.get("minorCode"))) {
                throw new CustomRuntimeException(StringUtils.join("[", em.get("category"), "]", "항목은 없는 인텐트 분류입니다."));
            }
            try {
                em.put("oprMngCode", oprMngCode);
                em.put("category", categoryMap.get("minorCode"));
                em.put("menuVal", "rcpconversationintentmanage");
//                this.insertIntent(em);
                Long id = this.insertIntentMasterExcel(em);
                ArrayList<IntentRequestDTO> exampleList = (ArrayList<IntentRequestDTO>) em.get("exampleList");
                if (!exampleList.isEmpty()) {
                    for (IntentRequestDTO intentRequestDTO : exampleList) {
                        HashMap example = new HashMap<>();
                        long soeId = 12345678; // soeID
                        example.put("intentName",em.get("intentName"));
                        example.put("intentId", id);
                        example.put("soeId", soeId);
                        example.put("sentence", intentRequestDTO.getExampleText());
                        exList.add(example);
                    }
                }
            } catch (DuplicateKeyException e) {
                throw new CustomRuntimeException(StringUtils.join("[", em.get("intentName"), "]", "는 중복된 인텐트 명입니다."));
            }
        }
        eMap.put("sentenceList",exList);
        return this.insertIntentExampleAll(eMap);
    }

    private Long insertIntentMasterExcel(HashMap eMap) {
        int cnt = this.selectIntentCnt(eMap);
        if (cnt > 0) {
            long id = intentManageMapper.selectIntentMappingId(eMap);
            return id;
        } else {
            // NLP API
            String oprMngCode = eMap.get("custCode") + "_RCP_" + eMap.get("serviceGroup");
            eMap.put("oprMngCode", oprMngCode);
            NlpWisenutResult nlpWisenutResult = nlpWisenutIntentService.intentInsertApi(eMap);
            if (nlpWisenutResult.getResult()) {
                eMap.put("mappingId",nlpWisenutResult.getMappingId());
                intentManageMapper.insertIntentMaster(eMap);
//                this.workLogSave(MenuResource.rcpconversationintentmanage, ActionResource.insert, String.valueOf(eMap.get("serviceGroup")), "인텐트 등록");
                return nlpWisenutResult.getMappingId();
            } else if (nlpWisenutResult.getReturnCode().equals("002")) {
                log.error(nlpWisenutResult.getReturnCode() + " : " + nlpWisenutResult.getReturnMsg());
                throw new DuplicateKeyException(StringUtils.join("[", eMap.get("intentName"), "]", "는 중복된 인텐트 명입니다."));
            } else {
                log.error(nlpWisenutResult.getReturnCode() + " : " + nlpWisenutResult.getReturnMsg());
                throw new DuplicateKeyException(StringUtils.join("[", eMap.get("intentName"), "]", "인텐트 : ", nlpWisenutResult.getReturnMsg()));
            }
        }
    }


    public String insertIntentExampleAll(HashMap eMap) {

        int successCount = 0;
        int dupCount = 0;
        int failCount = 0;
        // NLP API
        NlpWisenutResult nlpWisenutResult = nlpWisenutIntentService.intentSentenceInsertAllApi(eMap);
        if (nlpWisenutResult.getResult()) {
            for (SentenceListVO sentenceList : nlpWisenutResult.getSentenceList()) {
                if (sentenceList.getReturnCode().equals("000")) {
                    HashMap<String, Object> iMap = new HashMap<>();
                    String intentName = sentenceList.getIntentName();
                    iMap.put("intentName", intentName);
                    iMap.put("oprMngCode", eMap.get("oprMngCode"));
                    int seq = intentManageMapper.selectIntentSeq(iMap);

                    iMap.put("intentSeq", seq);
                    iMap.put("mappingId", sentenceList.getSentenceId());
                    iMap.put("registId",eMap.get("registId"));

                    List<HashMap> exampleList = new ArrayList<>();
                    HashMap tMap = new HashMap<>();
                    tMap.put("exampleText",sentenceList.getSentence());
                    exampleList.add(tMap);
                    iMap.put("exampleList",exampleList);

                    intentManageMapper.insertIntentExample(iMap);
                    successCount++;
                } else if (sentenceList.getReturnCode().equals("002")) {
                    dupCount++;
                } else {
                    failCount++;
                }
            }
//            logService.LogSave(LogVO.builder()
//                    .menuAuth(MenuResource.valueOf(String.valueOf(eMap.get("menuVal"))))
//                    .actionCode(String.valueOf((ActionResource.insert)))
//                    .serviceGroup(String.valueOf(eMap.get("serviceGroup")))
//                    .remark("예문 등록")
//                    .build());
        } else {
            log.error(nlpWisenutResult.getReturnCode() + " : " + nlpWisenutResult.getReturnMsg());
            throw new DuplicateKeyException(nlpWisenutResult.getReturnMsg());
        }

        if (successCount == 0) {
            throw new CustomRuntimeException(StringUtils.join("업로드에 실패했습니다.(중복 : " + dupCount + "건 실패 : "  + failCount + "건)"));
        }

        return "저장되었습니다. (성공 : " + successCount + "건 중복 : " + dupCount +"건 실패 : " + failCount + "건)";
    }

    public void downloadExcel(IntentSearchVo dto) throws IOException, CustomNotFoundException {


        dto.setServiceGroupUpper(tbServiceGrouopRepo.findByServiceGroup(dto.getServiceGroup()).orElseThrow(() -> new CustomNotFoundException()).getServiceGroupUpper());
//        List<ExcelIntentListVo> list = tbIntentMasterRepo.excelIntentList(dto);

        List<ExcelIntentListVoEx> exceldata = tbIntentMasterRepo.excelIntentList(dto).stream().map(e ->
                        e.toEx(tbIntentExampleRepo.findByIntentSeqAndUseYn(e.getSeq(), "Y")
                                .stream().map(f -> f.getExampleText())
                                .collect(Collectors.joining(","))
                        ))
                .collect(Collectors.toList());
        List<String> colName = new ArrayList<>();

        colName.add("인텐트 명");
        colName.add("예문");
        colName.add("분류");
        excelService.ExcelDownload("excel_intent", exceldata, colName);
        workLogSave(MenuResource.rcpconversationintentmanage, ActionResource.download, dto.getServiceGroup(), "엑셀 다운로드");
    }

    public void workLogSave(MenuResource menuAuth, ActionResource actionCode, String serviceCode, String remark) {
        logService.LogSave(LogVO.builder()
                .menuAuth(menuAuth)
                .actionCode(String.valueOf((actionCode)))
                .serviceGroup(serviceCode)
                .remark(remark)
                .build());
    }

}
