package com.viettel.campaign.service.impl;

import com.viettel.campaign.config.DataSourceQualify;
import com.viettel.campaign.model.ccms_full.*;
import com.viettel.campaign.repository.ccms_full.*;
import com.viettel.campaign.service.ScenarioQuestionService;
import com.viettel.campaign.service.ScenarioService;
import com.viettel.campaign.utils.BundleUtils;
import com.viettel.campaign.utils.Constants;
import com.viettel.campaign.utils.DataUtil;
import com.viettel.campaign.utils.WorkBookBuilder;
import com.viettel.campaign.web.dto.*;
import com.viettel.econtact.filter.UserSession;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.ss.util.CellRangeAddressList;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.modelmapper.ModelMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.file.Files;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @author anhvd_itsol
 */

@Service
@Transactional(rollbackFor = Exception.class)
public class ScenarioServiceImpl implements ScenarioService {
    private static final Logger logger = LogManager.getLogger(ScenarioServiceImpl.class);

    @Autowired
    ScenarioRepository scenarioRepository;

    @Autowired
    ScenarioQuestionRepository questionRepository;

    @Autowired
    ScenarioAnswerRepository answerRepository;

    @Autowired
    ModelMapper modelMapper;

    @Autowired
    ContactQuestResultRepository questResultRepository;

    @Autowired
    ScenarioQuestionService questionService;

    @Override
    @Transactional(DataSourceQualify.CCMS_FULL)
    public Scenario findScenarioByCampaignIdAndCompanySiteId(Long campaignId, Long companySiteId) {
        try {
            return scenarioRepository.findScenarioByCampaignIdAndCompanySiteId(campaignId, companySiteId);
        } catch (Exception ex) {
            logger.error(ex.getMessage(), ex);
        }
        return null;
    }

    @Override
    @Transactional(DataSourceQualify.CCMS_FULL)
    public ResultDTO update(ScenarioDTO scenarioDTO) {
        ResultDTO resultDTO = new ResultDTO();
        try {
            scenarioDTO.setCode(scenarioDTO.getCode().trim());
            if (scenarioDTO.getDescription() != null) scenarioDTO.setDescription(scenarioDTO.getDescription().trim());
            else scenarioDTO.setDescription("");
            scenarioDTO.setUpdateBy(null);
            scenarioDTO.setUpdateTime(new Date());

            Scenario scenario = modelMapper.map(scenarioDTO, Scenario.class);
            scenarioRepository.save(scenario);
            resultDTO.setData(scenario);
            resultDTO.setErrorCode(Constants.ApiErrorCode.SUCCESS);
            resultDTO.setDescription(Constants.ApiErrorDesc.SUCCESS);
        } catch (Exception ex) {
            logger.error(ex.getMessage(), ex);
            resultDTO.setErrorCode(Constants.ApiErrorCode.ERROR);
            resultDTO.setDescription(Constants.ApiErrorDesc.ERROR);
        }
        return resultDTO;
    }

    @Override
    @Transactional(DataSourceQualify.CCMS_FULL)
    public ResultDTO sortQuestionAndAnswer(ScenarioDTO scenarioDTO) {
        ResultDTO resultDTO = new ResultDTO();
        try {
            List<ScenarioQuestionDTO> lstQuestion = scenarioDTO.getLstQuestion();
            List<ScenarioAnswerDTO> lstAnswer = scenarioDTO.getLstAnswer();
            List<ScenarioQuestion> questionsToSort = new ArrayList<>();
            List<ScenarioAnswer> answersToSort = new ArrayList<>();
            if (lstQuestion.size() > 0) {
                lstQuestion.forEach(item -> {
                    ScenarioQuestion question = questionRepository.findScenarioQuestionByScenarioQuestionId(item.getScenarioQuestionId());
                    if (question != null) {
                        question.setOrderIndex(item.getOrderIndex());
                        questionsToSort.add(question);
                    }
                });
                questionRepository.saveAll(questionsToSort);
            }
            if (lstAnswer.size() > 0) {
                lstAnswer.forEach(item -> {
                    ScenarioAnswer answer = answerRepository.findScenarioAnswerByScenarioAnswerId(item.getScenarioAnswerId());
                    if (answer != null) {
                        answer.setOrderIndex(item.getOrderIndex());
                        answersToSort.add(answer);
                    }
                });
                answerRepository.saveAll(answersToSort);
            }

            resultDTO.setErrorCode(Constants.ApiErrorCode.SUCCESS);
            resultDTO.setDescription(Constants.ApiErrorDesc.SUCCESS);
        } catch (Exception ex) {
            logger.error(ex.getMessage(), ex);
            resultDTO.setErrorCode(Constants.ApiErrorCode.ERROR);
            resultDTO.setDescription(Constants.ApiErrorDesc.ERROR);
        }
        return resultDTO;
    }

    @Override
    @Transactional(DataSourceQualify.CCMS_FULL)
    public Integer countDuplicateScenarioCode(Long companySiteId, String code, Long scenarioId) {
        try {
            return scenarioRepository.countDuplicateScenarioCode(companySiteId, code, scenarioId);
        } catch (Exception ex) {
            logger.error(ex.getMessage(), ex);
        }
        return null;
    }

    public ResultDTO saveContacQuestResult(ContactQuestResultDTO dto) {
        ResultDTO resultDTO = new ResultDTO();

        if (dto != null) {
            ContactQuestResult cqr = questResultRepository.save(modelMapper.map(dto, ContactQuestResult.class));
            resultDTO.setErrorCode(Constants.ApiErrorCode.SUCCESS);
            resultDTO.setDescription(Constants.ApiErrorDesc.SUCCESS);
            resultDTO.setData(cqr);
        } else {
            resultDTO.setErrorCode(Constants.ApiErrorCode.ERROR);
            resultDTO.setDescription(Constants.ApiErrorDesc.ERROR);
        }

        return resultDTO;
    }

    @Override
    @Transactional(DataSourceQualify.CCMS_FULL)
    public XSSFWorkbook buildTemplate() {
        Locale locale = new Locale("vi", "VN");
        try {
            XSSFWorkbook workbook = new XSSFWorkbook();
            Sheet sheet;

            CellStyle styleTitle = WorkBookBuilder.buildDefaultStyleTitle(workbook);
            CellStyle styleRowHeader = WorkBookBuilder.buildDefaultStyleRowHeader(workbook);
            CellStyle styleRow = WorkBookBuilder.buildDefaultStyleRow(workbook);

            // list header
            List<String> fileHeaderList = new ArrayList<>();
            fileHeaderList.add(BundleUtils.getLangString("scenario.template.questionCode", locale));
            fileHeaderList.add(BundleUtils.getLangString("scenario.template.questionType", locale));
            fileHeaderList.add(BundleUtils.getLangString("scenario.template.question", locale));
            fileHeaderList.add(BundleUtils.getLangString("scenario.template.answer", locale));
            fileHeaderList.add(BundleUtils.getLangString("scenario.template.hashInput", locale));
            fileHeaderList.add(BundleUtils.getLangString("scenario.template.required", locale));
            fileHeaderList.add(BundleUtils.getLangString("scenario.template.default", locale));
            fileHeaderList.add(BundleUtils.getLangString("scenario.template.mappingQuestion", locale));
            //
            String sheetName = BundleUtils.getLangString("campaign", locale);
            sheet = workbook.createSheet(sheetName);

            // Title
            String title = BundleUtils.getLangString("scenario.template.title", locale);

            int rowTitleStart = 0;
            Row rowTitle = sheet.createRow(rowTitleStart);
            rowTitle.setHeight((short) 800);
            sheet.addMergedRegion(new CellRangeAddress(rowTitleStart, rowTitleStart, 0, 7));
            WorkBookBuilder.writeCellContent(rowTitle, styleTitle, 0, title);

            // Header
            int startRowTable = 2;
            Row rowHeader = sheet.createRow(startRowTable);
            for (int i = 0; i < fileHeaderList.size(); i++) {
                sheet.setColumnWidth(i, 6000);
                WorkBookBuilder.writeCellContent(rowHeader, styleRowHeader, i, fileHeaderList.get(i));
            }
            DataValidationHelper dataValidationHelper = sheet.getDataValidationHelper();

            //QuestionType
            DataValidationConstraint questionType = dataValidationHelper.createExplicitListConstraint(new String[]{BundleUtils.getLangString("scenario.template.singleChoice", locale),
                    BundleUtils.getLangString("scenario.template.multiChoice", locale), BundleUtils.getLangString("scenario.template.text", locale)});
            CellRangeAddressList typeCellRangeAddressList = new CellRangeAddressList(3, 9999, 1, 1);
            DataValidation typeValidation = dataValidationHelper.createValidation(questionType, typeCellRangeAddressList);
            typeValidation.setSuppressDropDownArrow(true);
            sheet.addValidationData(typeValidation);

            //HashInputCombobox
            DataValidationConstraint hasInput = dataValidationHelper.createExplicitListConstraint(new String[]{BundleUtils.getLangString("scenario.template.yes", locale), BundleUtils.getLangString("scenario.template.no", locale)});
            CellRangeAddressList hasInputCellRangeAddressList = new CellRangeAddressList(3, 9999, 4, 4);
            DataValidation hasInputValidation = dataValidationHelper.createValidation(hasInput, hasInputCellRangeAddressList);
            hasInputValidation.setSuppressDropDownArrow(true);
            sheet.addValidationData(hasInputValidation);

            //Required
            DataValidationConstraint required = dataValidationHelper.createExplicitListConstraint(new String[]{BundleUtils.getLangString("scenario.template.yes", locale), BundleUtils.getLangString("scenario.template.no", locale)});
            CellRangeAddressList requiredCellRangeAddressList = new CellRangeAddressList(3, 9999, 5, 5);
            DataValidation requiredValidation = dataValidationHelper.createValidation(required, requiredCellRangeAddressList);
            requiredValidation.setSuppressDropDownArrow(true);
            sheet.addValidationData(requiredValidation);

            //Required
            DataValidationConstraint defaultValue = dataValidationHelper.createExplicitListConstraint(new String[]{BundleUtils.getLangString("scenario.template.yes", locale), BundleUtils.getLangString("scenario.template.no", locale)});
            CellRangeAddressList defaultValueCellRangeAddressList = new CellRangeAddressList(3, 9999, 6, 6);
            DataValidation defaultValueValidation = dataValidationHelper.createValidation(defaultValue, defaultValueCellRangeAddressList);
            defaultValueValidation.setSuppressDropDownArrow(true);
            sheet.addValidationData(defaultValueValidation);


            return workbook;
        } catch (Exception e) {
            logger.error(e.getMessage());
            return null;
        }
    }

    @Override
    @Transactional(DataSourceQualify.CCMS_FULL)
    public Map<String, Object> readAndValidateCustomer(String path, Long scenarioId, Long campaignId, Long companySiteId) {
        Locale locale = new Locale("vi", "VN");
        SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
        Map<String, Object> result = new HashMap<>();
        StringBuilder sb = new StringBuilder();
        boolean isValid = true;
        try {
            List<String> fileHeaderList = new ArrayList<>();
            fileHeaderList.add(BundleUtils.getLangString("scenario.template.questionCode", locale));
            fileHeaderList.add(BundleUtils.getLangString("scenario.template.questionType", locale));
            fileHeaderList.add(BundleUtils.getLangString("scenario.template.question", locale));
            fileHeaderList.add(BundleUtils.getLangString("scenario.template.answer", locale));
            fileHeaderList.add(BundleUtils.getLangString("scenario.template.hashInput", locale));
            fileHeaderList.add(BundleUtils.getLangString("scenario.template.required", locale));
            fileHeaderList.add(BundleUtils.getLangString("scenario.template.default", locale));
            fileHeaderList.add(BundleUtils.getLangString("scenario.template.mappingQuestion", locale));

            File file = new File(path);
            FileInputStream fis = new FileInputStream(file);
            XSSFWorkbook workbook = new XSSFWorkbook(fis);
            ByteArrayOutputStream os = new ByteArrayOutputStream();
            Sheet sheet = workbook.getSheetAt(0);
            Row row = sheet.getRow(2);

            //<editor-fold desc="Tạo style cho cột kết quả" defaultstate="collapsed">
            Font resultFont = workbook.createFont();
            resultFont.setBold(true);
            resultFont.setFontHeightInPoints((short) 11);
            CellStyle resultStyle = workbook.createCellStyle();
            resultStyle.setFont(resultFont);
            resultStyle.setAlignment(HorizontalAlignment.CENTER);
            resultStyle.setFillForegroundColor(IndexedColors.LIGHT_GREEN.getIndex());
            resultStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
            resultStyle.setBorderRight(BorderStyle.THIN);
            resultStyle.setRightBorderColor(IndexedColors.BLACK.getIndex());
            resultStyle.setBorderBottom(BorderStyle.THIN);
            resultStyle.setBottomBorderColor(IndexedColors.BLACK.getIndex());
            resultStyle.setBorderLeft(BorderStyle.THIN);
            resultStyle.setLeftBorderColor(IndexedColors.BLACK.getIndex());
            resultStyle.setBorderTop(BorderStyle.THIN);
            resultStyle.setTopBorderColor(IndexedColors.BLACK.getIndex());

            //</editor-fold>

            //<editor-fold desc="Kiểm tra header của template" defaultstate="collapsed">
            for (int i = 0; i < fileHeaderList.size(); i++) {
                Cell cell = row.getCell(i);
                if (!cell.getStringCellValue().equals(fileHeaderList.get(i).split("#")[0])) {
                    result.put("content", Files.readAllBytes(file.toPath()));
                    result.put("code", Constants.FILE_UPLOAD_RESP_CODE.INVALID_FORMAT);
                    return result;
                }
            }
            //</editor-fold>

            //kt file du lieu rong
            if (sheet.getPhysicalNumberOfRows() == 2) {
                result.put("content", Files.readAllBytes(file.toPath()));
                result.put("code", Constants.FILE_UPLOAD_RESP_CODE.EMPTY);
                return result;
            } else {
                Cell resultCell = sheet.getRow(2).createCell(sheet.getRow(2).getPhysicalNumberOfCells());
                resultCell.setCellValue(BundleUtils.getLangString("customer.result", locale));
                resultCell.setCellStyle(resultStyle);
            }

            List<Object[]> rawDataList = new ArrayList<>();

            //validate row
            for (int i = 3; i <= sheet.getPhysicalNumberOfRows(); i++) {
                Row dataRow = sheet.getRow(i);
                if (dataRow != null) {
                    Object[] obj = new Object[row.getPhysicalNumberOfCells()];
                    for (int j = 0; j < row.getPhysicalNumberOfCells(); j++) {
                        Cell dataCell = dataRow.getCell(j);
                        if (dataCell != null) {
                            switch (dataCell.getCellTypeEnum()) {
                                case NUMERIC:
                                    obj[j] = dataCell.getNumericCellValue();
                                    break;
                                default:
                                    obj[j] = dataCell.getStringCellValue();
                                    break;
                            }
                        }
                    }

                    rawDataList.add(obj);
                }
            }

            //<editor-fold desc="Validate dữ liệu" defaultstate="collapsed">
            String selectedType = "";
            List<String> lstImportQuestionCodes = new ArrayList<>();
            String tmpCurrentQuestionCode = null;

            rawDataList.forEach(item -> {
                if (item[0] != null && !item[0].toString().trim().equals(""))
                    lstImportQuestionCodes.add(item[0].toString().trim());
            });

            for (int i = 0; i < rawDataList.size(); i++) {
                if (rawDataList.get(i)[0] != null && !rawDataList.get(i)[0].toString().trim().equals("")) {
                    selectedType = rawDataList.get(i)[1].toString().trim();
                    tmpCurrentQuestionCode = rawDataList.get(i)[0].toString().trim();
                }
                //validate question
                if ((rawDataList.get(i)[0] != null && !rawDataList.get(i)[0].toString().trim().equals("")) ||
                        (rawDataList.get(i)[1] != null && !rawDataList.get(i)[1].toString().trim().equals("")) ||
                        (rawDataList.get(i)[2] != null && !rawDataList.get(i)[2].toString().trim().equals("")) ||
                        (rawDataList.get(i)[5] != null && !rawDataList.get(i)[5].toString().trim().equals("")) ||
                        (rawDataList.get(i)[6] != null && !rawDataList.get(i)[6].toString().trim().equals(""))) {
                    if (rawDataList.get(i)[0] == null || !DataUtil.isNumber(rawDataList.get(i)[0].toString())) {
                        sb.append(BundleUtils.getLangString("scenario.questionCode.required"));
                    }
                    if (rawDataList.get(i)[1] == null || rawDataList.get(i)[1].toString().trim().equals("")) {
                        sb.append(BundleUtils.getLangString("scenario.questionType.required"));
                    }
                    if (rawDataList.get(i)[2] == null || rawDataList.get(i)[2].toString().trim().equals("")) {
                        sb.append(BundleUtils.getLangString("scenario.question.required"));
                    }
                    if (rawDataList.get(i)[5] == null || rawDataList.get(i)[5].toString().trim().equals("")) {
                        sb.append(BundleUtils.getLangString("scenario.required.required"));
                    }
                    if (rawDataList.get(i)[6] == null || rawDataList.get(i)[6].toString().trim().equals("")) {
                        sb.append(BundleUtils.getLangString("scenario.default.required"));
                    }
                }

                //validate answer
                if ((rawDataList.get(i)[3] != null && !rawDataList.get(i)[3].toString().trim().equals("")) ||
                        (rawDataList.get(i)[4] != null && !rawDataList.get(i)[4].toString().trim().equals("")) ||
                        (rawDataList.get(i)[7] != null && !rawDataList.get(i)[7].toString().trim().equals(""))) {
                    if ((rawDataList.get(i)[3] == null || rawDataList.get(i)[3].toString().trim().equals("")) && (!selectedType.equals(BundleUtils.getLangString("scenario.template.text")))) {
                        sb.append(BundleUtils.getLangString("scenario.answer.required"));
                    }
                    if (rawDataList.get(i)[4] == null && rawDataList.get(i)[4].toString().trim().equals("")) {
                        sb.append(BundleUtils.getLangString("scenario.hashInput.required"));
                    }
                    if ((rawDataList.get(i)[7] != null && !rawDataList.get(i)[7].toString().trim().equals(""))) {
                        if ((rawDataList.get(i)[7].toString().trim().equals(tmpCurrentQuestionCode)) ||
                                validateMappingQuestion(rawDataList.get(i)[7].toString().trim(), tmpCurrentQuestionCode, lstImportQuestionCodes))
                            sb.append(BundleUtils.getLangString("scenario.mappingQuestion.invalid"));
                    }

                }
                Row dataRow = sheet.getRow(3 + i);
                Cell resultCell = dataRow.createCell(row.getPhysicalNumberOfCells() - 1);
                if (sb.length() > 0) {
                    resultCell.setCellValue(sb.toString());
                    isValid = false;
                } else {
//                    isValid = true;
                    resultCell.setCellValue(BundleUtils.getLangString("ok"));
                }
                sb = new StringBuilder();
            }
            //</editor-fold>

            //insert data
            if (isValid) {
                List<ScenarioQuestionDTO> lstQuestion = buildQuestionsLst(rawDataList, scenarioId, campaignId, companySiteId);
                lstQuestion.forEach(question -> {
                    questionService.add(question);
                });
                workbook.write(os);
                os.flush();
                os.close();
                workbook.close();
                result.put("content", os.toByteArray());
                result.put("code", Constants.FILE_UPLOAD_RESP_CODE.SUCCESS);
            } else {
                workbook.write(os);
                os.flush();
                os.close();
                workbook.close();
                result.put("content", os.toByteArray());
                result.put("code", Constants.FILE_UPLOAD_RESP_CODE.ERROR);
            }
        } catch (Exception ex) {
            logger.info(ex.getMessage(), ex);
            result.put("code", Constants.FILE_UPLOAD_RESP_CODE.ERROR);
        }
        return result;
    }

    private boolean validateMappingQuestion(String mappingQuestion, String currentQuestionCode, List<String> lstQuestionCode) {
        if (mappingQuestion == currentQuestionCode) return false;

        String duplicateCode = lstQuestionCode.stream().
                filter(p -> (p.equals(mappingQuestion) && p.equals(currentQuestionCode))).
                findAny().orElse(null);
        if (duplicateCode == null) return false;

//        String notMappedCode = lstQuestionCode.stream().filter(p -> (!p.equals(mappingQuestion))).findAny().orElse(null);
//        if(notMappedCode != null) return false;
        return true;
    }


    private Long questionOrderIndex = null;
    private Integer answerIndex = null;

    private List<ScenarioQuestionDTO> buildQuestionsLst(List<Object[]> rawDatas, Long scenarioId, Long campaignId, Long companySiteId) {
        List<ScenarioQuestionDTO> lstQuestions = new ArrayList<>();
        List<ScenarioAnswerDTO> lstAnswerOfQuestion = new ArrayList<>();
        ScenarioQuestionDTO questionDTO = null;
        String questionCode = null;
        for (int i = 0; i < rawDatas.size(); i++) {
            if (rawDatas.get(i)[0] != null && !rawDatas.get(i)[0].toString().trim().equals("")) {
                questionCode = rawDatas.get(i)[0].toString().trim();
                questionDTO = new ScenarioQuestionDTO();
                questionDTO.setScenarioId(scenarioId);
                questionDTO.setCampaignId(campaignId);
                questionDTO.setCompanySiteId(companySiteId);
                questionDTO.setImportCode(questionCode);

                if (rawDatas.get(i)[1].toString().trim().equals(BundleUtils.getLangString("scenario.template.text")))
                    questionDTO.setType((short) 0);
                else if (rawDatas.get(i)[1].toString().trim().equals(BundleUtils.getLangString("scenario.template.singleChoice")))
                    questionDTO.setType((short) 1);
                else questionDTO.setType((short) 2);

                questionDTO.setQuestion(rawDatas.get(i)[2].toString().trim());

                if (rawDatas.get(i)[5].toString().trim().equals(BundleUtils.getLangString("scenario.template.yes")))
                    questionDTO.setIsRequire((short) 1);
                else questionDTO.setIsRequire((short) 0);

                if (rawDatas.get(i)[6].toString().trim().equals(BundleUtils.getLangString("scenario.template.yes")))
                    questionDTO.setIsDefault((short) 1);
                else questionDTO.setIsDefault((short) 0);

                lstQuestions.add(questionDTO);
            } else {
                ScenarioAnswerDTO answerDto = new ScenarioAnswerDTO();
                answerDto.setAnswer(rawDatas.get(i)[3].toString().trim());
                if (rawDatas.get(i)[4].toString().trim().equals(BundleUtils.getLangString("scenario.template.yes")))
                    answerDto.setHasInput((short) 1);
                else answerDto.setHasInput((short) 0);
                if (rawDatas.get(i)[7] != null && !rawDatas.get(i)[7].toString().trim().equals("")) {
                    answerDto.setMappingQuestionCode(rawDatas.get(i)[7].toString().trim());
                }
                answerDto.setImportQuestionCode(questionCode);
                answerDto.setCompanySiteId(companySiteId);

                lstAnswerOfQuestion.add(answerDto);
            }
        }

        questionOrderIndex = questionRepository.getMaxOrderId(scenarioId, campaignId, companySiteId);

        if (questionOrderIndex == null) questionOrderIndex = 0L;
        lstQuestions.forEach(q -> {
            questionOrderIndex += 1;
            q.setOrderIndex(questionOrderIndex);
            q.setCode(campaignId + "_" + questionOrderIndex);
            answerIndex = 0;
            List<ScenarioAnswerDTO> answers = new ArrayList<>();
            lstAnswerOfQuestion.forEach(a -> {
                answerIndex += 1;
                a.setOrderIndex(answerIndex);
                if (a.getImportQuestionCode().equals(q.getImportCode())) answers.add(a);
            });
            q.setLstAnswers(answers);
        });
        questionOrderIndex = null;
        return lstQuestions;
    }

}
