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

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.rcp.conversation.nlp.wisenut.service.NlpWisenutService;
import com.icomsys.main_vm.biz.rcp.conversation.nlp.wisenut.vo.InferenceAPIResponseDTO;
import com.icomsys.main_vm.biz.rcp.learningTest.vo.SelectProjectSettingInfoVO;
import com.icomsys.main_vm.common.code.enumresource.ActionResource;
import com.icomsys.main_vm.common.code.enumresource.MenuResource;
import com.icomsys.main_vm.common.util.ExcelUtils;
import com.icomsys.main_vm.db.jpa.repo.conversation.TbProjectSettingRepo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.RegExUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.xssf.usermodel.XSSFFont;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.*;

@Service
@Slf4j
@RequiredArgsConstructor
public class LearningTestInferenceService {

    private final TbProjectSettingRepo tbProjectSettingRepo;
    private final LogService logService;

    @Resource(name = "nlpWisenutService")
    private NlpWisenutService nlpAPIService;


    /**
     * 유저가 업로드한 엑셀 파일 Read
     */
    private List<String> readExcelFile(MultipartFile file) throws Exception {
        try {
            XSSFWorkbook wb = new XSSFWorkbook(file.getInputStream());
            XSSFSheet sheet = wb.getSheetAt(1);

            List<String> testList = new ArrayList<String>();
            for (int i = 1; i < sheet.getPhysicalNumberOfRows(); i++) {
                XSSFRow row = sheet.getRow(i);
                if(row == null) continue;
                if(row.getCell(0) == null) continue;

                row.getCell(0).setCellType(CellType.STRING);
                String v = row.getCell(0).getStringCellValue();

                // 한글/영문/공백 제외 삭제처리
    //            v = StringUtils.replacePattern(v, "[^\\uAC00-\\uD7A30-9a-zA-Z\\s]", "");
                v = RegExUtils.replacePattern(v, "[^\\uAC00-\\uD7A30-9a-zA-Z\\s]", "");
                testList.add(v);
            }

            return testList;
        } catch (Exception e) {
//            throw new Exception("올바른 엑셀 형식이 아닙니다.");
            throw new CustomRuntimeException("올바른 엑셀 형식이 아닙니다.");
        }
    }

    /**
     * 의도추론 테스트 API 호출
     */
    private InferenceAPIResponseDTO callInferenceAPI(List<String> testList, HashMap<String, String> eMap) {
        String oprMngCode = eMap.get("oprMngCode") == null ? null : eMap.get("oprMngCode");

        // NLP API 호출
        SelectProjectSettingInfoVO projectSettingInfo = tbProjectSettingRepo.selectProjectSettingInfo(oprMngCode);
        Long projectId = projectSettingInfo.getIfMappingId();
        if (projectId == null) {
            return new InferenceAPIResponseDTO();
        }

        // API 관련 데이터
        // FIXME: 고객사(UPLUS) 셀렉트 값 문제 해결 후 아래 코드로 변경 필요.
        InferenceAPIResponseDTO responseDTO = nlpAPIService.multiQueryApi(projectId.intValue(), testList.toArray(new String[testList.size()]));
        log.info("# API RESULT MAP : {}", responseDTO.toString());
        return responseDTO;
    }

    /**
     * 의도추론 테스트 API 호출
     */
    private InferenceAPIResponseDTO testCallInferenceAPI() {

        InferenceAPIResponseDTO responseDTO = new InferenceAPIResponseDTO();

        responseDTO.setReturnCode("000");
        responseDTO.setReturnMsg("Success");
        List<InferenceAPIResponseDTO> rlist = new ArrayList<>();

        InferenceAPIResponseDTO responseDTO1 = new InferenceAPIResponseDTO();
        responseDTO1.setDialogTaskId("90922661");
        responseDTO1.setDialogTaskName("영업시간 안내");
        responseDTO1.setQuery("오늘 영업하나요?");

        InferenceAPIResponseDTO responseDTO2 = new InferenceAPIResponseDTO();
        responseDTO2.setDialogTaskId("90923772");
        responseDTO2.setDialogTaskName("주차위치 안내");
        responseDTO2.setQuery("주차장 있나요");

        rlist.add(responseDTO1);
        rlist.add(responseDTO2);

        responseDTO.setData(rlist);
        return responseDTO;
    }

    /**
     * API 응답 결과 엑셀파일 생성
     */
    private XSSFWorkbook createResultExcel(InferenceAPIResponseDTO responseDTO) {
        // Excel 파일 생성
        XSSFWorkbook wbResult = new XSSFWorkbook();
        XSSFFont font = wbResult.createFont();
        XSSFSheet resultSheet = wbResult.createSheet("data");
        XSSFRow row = resultSheet.createRow(0);

        // 셀 헤더
        ExcelUtils.createDefaultHeaderCellStyle(row, 0, font, "의도추론 대상 문장");
        ExcelUtils.createDefaultHeaderCellStyle(row, 1, font, "인텐트");
        ExcelUtils.createDefaultHeaderCellStyle(row, 2, font, "인텐트ID");

        List<InferenceAPIResponseDTO> dataList = responseDTO.getData();
        int rowNum = 1;
        for(InferenceAPIResponseDTO data : dataList) {
            row = resultSheet.createRow(rowNum++);
            ExcelUtils.createDefaultValueCellStyle(row, 0, font, data.getQuery());
            ExcelUtils.createDefaultValueCellStyle(row, 1, font, data.getDialogTaskName());
            ExcelUtils.createDefaultValueCellStyle(row, 2, font, data.getDialogTaskId());
        }

        // 열너비 조정
        resultSheet.autoSizeColumn(0);
        resultSheet.setColumnWidth(0, (resultSheet.getColumnWidth(0))+(short)8192);
        resultSheet.autoSizeColumn(1);
        resultSheet.setColumnWidth(1, (resultSheet.getColumnWidth(1))+(short)4096);
        resultSheet.autoSizeColumn(2);
        resultSheet.setColumnWidth(2, (resultSheet.getColumnWidth(2))+(short)1024);

        return wbResult;
    }


    public void testInferenceExcel(MultipartHttpServletRequest request, HttpServletResponse response, HashMap<String, String> eMap) throws Exception {
        // excel 관련
        MultipartFile file = request.getFile("upFile");

        try {
            List<String> testList = new ArrayList<>();
            try {
                testList = this.readExcelFile(file);

            } catch (CustomRuntimeException e) {
                response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); // xlsx
                response.setHeader("Content-disposition", "attachment; filename=" + "notexcel");
                response.setHeader("Content-Transfer-Encoding", "binary");
                response.setHeader("Connection", "close");
                throw new CustomRuntimeException("올바른 엑셀 형식이 아닙니다.");
            }
            if (CollectionUtils.isEmpty(testList)) {
                response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); // xlsx
                response.setHeader("Content-disposition", "attachment; filename=" + "noexapmle");
                response.setHeader("Content-Transfer-Encoding", "binary");
                response.setHeader("Connection", "close");
                throw new CustomRuntimeException("입력된 문장이 없습니다.");
            }
            if (testList.size() > 200) {
                response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); // xlsx
                response.setHeader("Content-disposition", "attachment; filename=" + "sizeover");
                response.setHeader("Content-Transfer-Encoding", "binary");
                response.setHeader("Connection", "close");
                throw new CustomRuntimeException("의도추론 예문 제한을 초과했습니다.");
            }

            // file 관련
            Calendar c = Calendar.getInstance();
            c.clear();
            c.setTime(new Date());
            SimpleDateFormat sdfForPath = new SimpleDateFormat("yyyy/MM/");

            // API 관련 데이터
            InferenceAPIResponseDTO responseDTO = this.callInferenceAPI(testList, eMap);

            // API 관련 데이터 로컬용
//        InferenceAPIResponseDTO responseDTO = this.testCallInferenceAPI();

            String returnCode = responseDTO.getReturnCode();
            String testResult = StringUtils.equals(returnCode, "000") ? "SUCCESS" : "FAIL";


            // 성공
            if (StringUtils.equals(returnCode, "000")) {
                eMap.put("testResult", testResult);

                XSSFWorkbook wbResult = this.createResultExcel(responseDTO);
                SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss");
                String now = sdf.format(new Date());
                String resultFileName = URLEncoder.encode("test_inference_result_" + now + ".xlsx", "UTF-8");


                resultFileName = resultFileName.replaceAll("/", "").replaceAll("&", "").replaceAll("\\\\", "").replaceAll("/[.]{1,2}", "").replaceAll("\\[.]{1,2}", ""); // Path Manipulation

                String filename = URLEncoder.encode(resultFileName, "UTF-8");
                response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); // xlsx
                response.setHeader("Content-disposition", "attachment; filename=" + filename);
                response.setHeader("Content-Transfer-Encoding", "binary");
                response.setHeader("Connection", "close");

//            XSSFWorkbook wb = new XSSFWorkbook(mf.getInputStream());
                wbResult.write(response.getOutputStream());
                wbResult.close();

                logService.LogSave(LogVO.builder()
                        .menuAuth(MenuResource.rcplearningTestinferencemanage)
                        .actionCode(String.valueOf((ActionResource.download)))
                        .serviceGroup(eMap.get("oprMngCode").split("_")[2])
                        .build());
            }
            // 실패
            else {
                eMap.put("testResult", "false");
            }
        } catch (CustomRuntimeException e) {

            throw e;
        } catch (Exception e) {
            throw e;
        }
//        this.insertInference(eMap);
    }
}
