package com.ruoyi.project.common.controller;

import static com.ruoyi.common.importExcel.excel.TableTypeEnum.Elevator_Model_Date;
import static com.ruoyi.common.importExcel.excel.TableTypeEnum.TRAIN_QUESTION_EXCEL;
import static com.ruoyi.common.importExcel.excel.TableTypeEnum.getEnum;
import static com.ruoyi.common.importExcel.excel.TableTypeEnum.getFileNames;
import static com.ruoyi.common.importExcel.utils.ExcelUtils.UploadResult;
import static com.ruoyi.common.importExcel.utils.ExcelUtils.createWorkbook;
import static com.ruoyi.common.importExcel.utils.ExcelUtils.readData;
import static java.util.stream.Collectors.toList;

import com.alibaba.fastjson2.JSON;
import com.google.common.collect.Lists;
import com.ruoyi.common.exception.ServiceErrorException;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.importExcel.excel.BasicUploadParam;
import com.ruoyi.common.importExcel.excel.EasyPoiHelper;
import com.ruoyi.common.importExcel.excel.TableTypeEnum;
import com.ruoyi.common.importExcel.excel.download.BasicDownloadParams;
import com.ruoyi.common.importExcel.excel.helper.BasicExcelHelper;
import com.ruoyi.common.importExcel.excel.helper.BasicTableInfoHelper;
import com.ruoyi.common.importExcel.excel.helper.PriceFormTableInfoHelper;
import com.ruoyi.common.importExcel.excel.tempInfo.AccountInfoHelper;
import com.ruoyi.common.importExcel.excel.tempInfo.ElevatorParaInfoHelper;
import com.ruoyi.common.importExcel.excel.tempInfo.LeadInfoHelper;
import com.ruoyi.common.importExcel.excel.tempInfo.ParaLibraryExcel;
import com.ruoyi.common.importExcel.excel.tempInfo.ParaLibraryExportHelper;
import com.ruoyi.common.importExcel.excel.tempInfo.ParaRelatedCaseActionHelper;
import com.ruoyi.common.importExcel.excel.tempInfo.ParaRelatedCaseInfoHelper;
import com.ruoyi.common.importExcel.excel.tempInfo.ParaRelatedCaseLogicHelper;
import com.ruoyi.common.importExcel.excel.tempInfo.ParaValidationInfoHelper;
import com.ruoyi.common.importExcel.excel.tempInfo.PriceFormBasicInfoHelper;
import com.ruoyi.common.importExcel.excel.tempInfo.PriceFormOptionInfoHelper;
import com.ruoyi.common.importExcel.excel.tempInfo.ProjectFromInfo;
import com.ruoyi.common.importExcel.excel.tempInfo.TrainQuestionHelper;
import com.ruoyi.common.importExcel.excel.validation.BasicDataValidation;
import com.ruoyi.common.importExcel.utils.FileUtils;
import com.ruoyi.common.utils.MessageUtils;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.project.business.domain.TpAreaParaSelectItem;
import com.ruoyi.project.business.domain.train.TtQuestionDatabase;
import com.ruoyi.project.business.service.ITcAccountService;
import com.ruoyi.project.business.service.ITcLeadService;
import com.ruoyi.project.business.service.ITcProjectService;
import com.ruoyi.project.business.service.ITpAreaBasicPriceService;
import com.ruoyi.project.business.service.ITpAreaElevatorModelParaService;
import com.ruoyi.project.business.service.ITpElevatorModelService;
import com.ruoyi.project.business.service.ITpParaLibraryService;
import com.ruoyi.project.business.service.ITpParaRelatedCaseService;
import com.ruoyi.project.business.service.ITpParaValidationService;
import com.ruoyi.project.business.service.train.ITtQuestionDatabaseService;
import com.ruoyi.project.common.domain.CheckVO;
import com.ruoyi.project.common.service.ICommonService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.function.BiFunction;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.BeanUtils;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.http.MediaType;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

/**
 * 前端控制器
 *
 * @author CAC
 * @date 2021-01-15 12:00:00
 */
@Api(tags = "Excel", value = "excel相关接口")
@RestController
@RequestMapping(value = "/excel", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
@Validated
public class ExcelRestController {

    @Resource
    private FileUtils fileUtils;

    @Resource
    private ICommonService commonService;

    @Resource
    private ITpParaLibraryService tpParaLibraryService;

    @Resource
    private ITpAreaElevatorModelParaService tpAreaElevatorModelParaService;

    @Resource
    private ITcAccountService tcAccountService;

    @Resource
    private ITcLeadService tcLeadService;

    @Resource
    private ITpElevatorModelService tpElevatorModelService;
    @Resource
    private ITpAreaBasicPriceService tpAreaBasicPriceService;
    @Resource
    private ITtQuestionDatabaseService iTtQuestionDatabaseService;
    @Resource
    private ITpParaValidationService tpParaValidationService;

    @Resource
    private ITpParaRelatedCaseService tpParaRelatedCaseService;

    @Resource
    private ITcProjectService tcProjectService;


    @ApiOperation(value = "下载模板信息")
    @PostMapping("/downloadTemplate")
    public void downloadTemplate(HttpServletRequest request, HttpServletResponse response, @RequestBody BasicDownloadParams params) {
        TableTypeEnum tableType;

        Locale locale = LocaleContextHolder.getLocale();
        try {
            //tableType = getEnum(valueOfName(tableName));
            //tableType = getEnum(valueOfName(tableName, locale));
            tableType = getEnum(params.getTableType());
        } catch (Exception e) {
            e.printStackTrace();
            //下载失败，模板不存在
            throw new ServiceErrorException(MessageUtils.message("excel.message.filenotexist"));
        }

        // 1. 每个List对应一个sheet中的数据
        List<List<? extends BasicExcelHelper>> data = Lists.newArrayList();
        // 2. 基本导入信息
        List<BasicTableInfoHelper> tableInfoList = Lists.newArrayList();
        BasicTableInfoHelper tableInfo = new BasicTableInfoHelper();
        // 3. 数据验证等相关表单限制
        BasicDataValidation dataValidation = null;

        String fileName = "";

         switch (tableType) {
             case ACCOUNT_EXCEL:
                break;
             case ParaLibrary_Excel://参数库
                 if (params.getWithData()){
                     //设置查询参数
                     List<List<? extends BasicExcelHelper>> result = Lists.newArrayList();
                     List<ParaLibraryExportHelper> list = tpParaLibraryService.selectTpParaLibraryListExport(params.getParams());
                     result.add(list.stream().map(item -> {
                         ParaLibraryExportHelper helper = new ParaLibraryExportHelper();
                         BeanUtils.copyProperties(item, helper);
                         return helper;
                     }).collect(Collectors.toList()));
                     data.addAll(result);
                     //如果是下载带数据的，则使用导出模板
                     tableType = Elevator_Model_Date;
                 }
                 break;
             case Elevator_Model_Excel://区域电梯导入
//                 if (params.getWithData()){
//                     if (StringUtils.isBlank(params.getElevatorModelId())) throw new ServiceException("梯型不能为空");
//                     Long elevatorModelIds = Long.valueOf(params.getElevatorModelId());
//                     tableInfo = new PriceFormTableInfoHelper();
//                     BeanUtils.copyProperties(params, tableInfo);
//                     data.addAll(tpAreaElevatorModelParaService.elevatorModelExcel(elevatorModelIds));
//                 }
                 break;
             case LEAD_IMPORT_EXCEL://线索信息
                 break;
             case PRICE_FORM_EXCEL://价格表
                 if (StringUtils.isBlank(params.getElevatorModelId())) throw new ServiceException("梯型不能为空");
                 Long elevatorModelId = Long.valueOf(params.getElevatorModelId());
                 tableInfo = new PriceFormTableInfoHelper();
                 BeanUtils.copyProperties(params, tableInfo);
                 if (params.getWithData()){
                     data.addAll(tpAreaBasicPriceService.getPriceForm(elevatorModelId));
                 }
                 break;
             case TRAIN_QUESTION_EXCEL:
                 break;
             case Elevator_Model_Date://区域电梯参数详情
                 if (StringUtils.isBlank(params.getElevatorModelId())) throw new ServiceException("梯型不能为空");
                 if (params.getWithData()){
                     List<List<? extends BasicExcelHelper>> result = Lists.newArrayList();
                     TpAreaParaSelectItem param = new TpAreaParaSelectItem();
                     param.setElevatorModelId(Long.parseLong(params.getElevatorModelId()));
                     List<ParaLibraryExportHelper> list = tpParaLibraryService.selectModelExport(param);
                     result.add(list.stream().map(item -> {
                         ParaLibraryExportHelper helper = new ParaLibraryExportHelper();
                         BeanUtils.copyProperties(item, helper);
                         return helper;
                     }).collect(Collectors.toList()));
                     data.addAll(result);
                 }
                 break;
             case PARA_VALIDATION_EXCEL:
                 if (params.getWithData()){
                     data.addAll(tpParaValidationService.getParaValidationList());
                 }
                 break;
             case PARA_RELATED_CASE_EXCEL:
                 if (params.getWithData()){
                     data.addAll(tpParaRelatedCaseService.getRelatedCaseList());
                 }
                 break;
            case PROJECT_IMPORT_EXCEL:
                break;
             default:
                 //不支持的导入类型
                throw new ServiceErrorException(MessageUtils.message("excel.message.unsupport"));
        }
        // 设置基本导入信息
        if (Objects.nonNull(tableInfo)){
            tableInfo.setTableName(tableType.getName());
            tableInfoList.add(tableInfo);
            data.add(0, tableInfoList);
        }

        if (StringUtils.isBlank(fileName)){
            fileName = getFileNames(tableType, locale);
        }

        request.setAttribute("downloadName", params.getDownloadFileName());

        //fileUtils.downloadTemplate(request, response, params.getFileName(), data);
        fileUtils.downloadTemplate(request, response, fileName, data);
        //fileUtils.downloadTemplate(request, response, fileUtils.getFileNames(tableType, locale), data);
    }

    /**
     * 读取excel获取基本导入信息和导入数据并返回
     *
     * @param file
     * @return
     */
    @ApiOperation(value = "校验excel信息")
    @PostMapping("/check")
    public AjaxResult check(CheckVO checkVO, MultipartFile file) {
        XSSFWorkbook workbook = createWorkbook(file);

        // 1.获取基本导入信息(用于区分不同的导入，如有额外信息需要在各自处理逻辑中重新读取)
        XSSFSheet sheet = workbook.getSheetAt(0);
        BasicTableInfoHelper tableInfo = getTableInfo(sheet, BasicTableInfoHelper.class);

        // 2.获取所有导入数据表
        List<XSSFSheet> sheets = Lists.newArrayList();
        for (int i = 0; i < workbook.getNumberOfSheets(); i++) {
            if (!workbook.isSheetHidden(i)){
                sheets.add(workbook.getSheetAt(i));
            }
        }

        List<UploadResult> finalResult = Lists.newArrayList();
        switch (getEnum(Integer.valueOf(checkVO.getTableType()))) {
            case ACCOUNT_EXCEL:
                tableInfo = getTableInfo(sheet, BasicTableInfoHelper.class);
                tableInfo.setCheckVO(checkVO);
                finalResult.add(checkSheets(tcAccountService::checkAccountInfo, tableInfo, sheets, AccountInfoHelper.class));
                break;
            case ParaLibrary_Excel:
                tableInfo = getTableInfo(sheet, BasicTableInfoHelper.class);
                List<ParaLibraryExcel> list = EasyPoiHelper.importExcel(file, 0, 2, ParaLibraryExcel.class);
                finalResult.add(checkSheetsByList(tpParaLibraryService::checkParaLibrary, tableInfo, list, ParaLibraryExcel.class));
                break;
            case Elevator_Model_Excel:
                tableInfo = getTableInfo(sheet, BasicTableInfoHelper.class);
                tableInfo.setCheckVO(checkVO);
                List<ParaLibraryExcel> listElevatorModel = EasyPoiHelper.importExcel(file, 0, 2, ParaLibraryExcel.class);
                //finalResult.add(checkSheets(tpAreaElevatorModelParaService::checkElevatorParaInfo, tableInfo, sheets, ElevatorParaInfoHelper.class));
                finalResult.add(checkSheetsByList(tpAreaElevatorModelParaService::checkElevatorParaInfoNew, tableInfo, listElevatorModel, ParaLibraryExcel.class));
                break;
            case LEAD_IMPORT_EXCEL:
                tableInfo = getTableInfo(sheet, BasicTableInfoHelper.class);
                tableInfo.setCheckVO(checkVO);
                finalResult.add(checkSheets(tcLeadService::checkLeadImport, tableInfo, sheets, LeadInfoHelper.class));
                break;
            case PRICE_FORM_EXCEL:
                tableInfo.setCheckVO(checkVO);
                finalResult.add(checkSheets(tpAreaBasicPriceService::checkBasicInfo, new PriceFormTableInfoHelper(checkVO.getElevatorModelId()), sheets.subList(0,1), PriceFormBasicInfoHelper.class));
                finalResult.add(checkSheets(tpAreaBasicPriceService::checkOptionInfo, new PriceFormTableInfoHelper(checkVO.getElevatorModelId()), sheets.subList(1,2), PriceFormOptionInfoHelper.class));
                break;
            case TRAIN_QUESTION_EXCEL:
                tableInfo = getTableInfo(sheet, BasicTableInfoHelper.class);
                tableInfo.setCheckVO(checkVO);
                finalResult.add(checkSheets(iTtQuestionDatabaseService::checkQuestionInfo, tableInfo, sheets, TrainQuestionHelper.class));
                break;
            case PARA_VALIDATION_EXCEL:
                tableInfo = getTableInfo(sheet, BasicTableInfoHelper.class);
                tableInfo.setCheckVO(checkVO);
                finalResult.add(checkSheets(tpParaValidationService::checkParaValidation, tableInfo, sheets, ParaValidationInfoHelper.class));
                break;
            case PARA_RELATED_CASE_EXCEL:
                tableInfo = getTableInfo(sheet, BasicTableInfoHelper.class);
                tableInfo.setCheckVO(checkVO);
                finalResult.add(checkSheets(tpParaRelatedCaseService::checkCaseInfo, tableInfo, sheets.subList(0,1), ParaRelatedCaseInfoHelper.class));
                finalResult.add(checkSheets(tpParaRelatedCaseService::checkCaseLogic, tableInfo, sheets.subList(1,2), ParaRelatedCaseLogicHelper.class));
                finalResult.add(checkSheets(tpParaRelatedCaseService::checkCaseAction, tableInfo, sheets.subList(2,3), ParaRelatedCaseActionHelper.class));
                break;
            case PROJECT_IMPORT_EXCEL:
                tableInfo = getTableInfo(sheet, BasicTableInfoHelper.class);
                List<ProjectFromInfo> projectFromInfoList = EasyPoiHelper.importExcel(file, 0, 2,
                    ProjectFromInfo.class);
                if (projectFromInfoList == null) {
                    finalResult.add(new UploadResult(new ArrayList<>(), new ArrayList(),
                        Collections.singletonList(MessageUtils.message("excel.message.readSheetFail")),
                        Collections.singletonList(MessageUtils.message("excel.message.readSheetFail"))));
                } else {
                    finalResult.add(checkSheetsByList(tcProjectService::checkProjectFormInfoImport, tableInfo,
                        projectFromInfoList,
                        ProjectFromInfo.class));
                }
                break;
            default:
                //不支持的导入类型
                throw new ServiceErrorException(MessageUtils.message("excel.message.unsupport"));
        }
        if (CollectionUtils.isEmpty(finalResult)){
            for (UploadResult item: finalResult){
                item.setData(tableInfo);
            }
        }
        if (finalResult.stream().map(UploadResult::getSuccessList).flatMap(Collection::stream).count() == 0 &&
                finalResult.stream().map(UploadResult::getErrorList).flatMap(Collection::stream).count() == 0) {
            throw new ServiceErrorException(MessageUtils.message("excel.message.templateNotEmpty"));
        }

        try {
            workbook.close();
        } catch (Exception e) {
            // ignore
        }
        return AjaxResult.success(finalResult);
    }


    @ApiOperation(value = "批量保存excel信息")
    @PostMapping("/saveBatch")
    public AjaxResult saveBatch(@RequestBody BasicUploadParam<HashMap> param) {
        Integer tableType = param.getTableType();
        if (Objects.isNull(tableType)) {
            //未获取到导入信息，请使用正确的模板
            throw new ServiceErrorException(MessageUtils.message("excel.message.rightFile"));
        }

        List<HashMap> dataList = param.getDataList();
        if (CollectionUtils.isEmpty(dataList)) {
            //throw new ServiceErrorException("导入模板不能为空");
            throw new ServiceErrorException(MessageUtils.message("excel.message.templateNotEmpty"));
        }

        List<String> errMsgs = new ArrayList<>();
        List<TtQuestionDatabase> resultList = Lists.newArrayList();
        switch (getEnum(tableType)) {
            case ACCOUNT_EXCEL:
                errMsgs = tcAccountService.insertTcAccountExcel(new BasicTableInfoHelper(),
                    transform(dataList, AccountInfoHelper.class));
                break;
            case ParaLibrary_Excel:
                errMsgs = tpParaLibraryService.insertTpParaLibraryExcel(transform(dataList, ParaLibraryExcel.class));
                break;
            case Elevator_Model_Excel:
                //errMsgs = tpAreaElevatorModelParaService.insertElevatorModelParaExcel(new BasicTableInfoHelper(), transform(dataList, ElevatorParaInfoHelper.class));
                errMsgs = tpAreaElevatorModelParaService.insertElevatorModelParaExcelNew(new BasicTableInfoHelper(), transform(dataList, ParaLibraryExcel.class));
                break;
            case LEAD_IMPORT_EXCEL:
                errMsgs = tcLeadService.insertLeadImportExcel(new BasicTableInfoHelper(),
                    transform(dataList, LeadInfoHelper.class));
                break;
            case PROJECT_IMPORT_EXCEL:
                List<ProjectFromInfo> transform = transform(dataList, ProjectFromInfo.class);
                errMsgs = tcProjectService.insertProjectImportExcel(new BasicTableInfoHelper(),
                    transform);
                break;
            case PRICE_FORM_EXCEL:
                errMsgs = tpAreaBasicPriceService.insertPriceFormImportExcel(new BasicTableInfoHelper(), param.getParam().getElevatorModelId(), dataList);
                break;
            case TRAIN_QUESTION_EXCEL:
                resultList = iTtQuestionDatabaseService.insertQuestionExcel(new BasicTableInfoHelper(), transform(dataList, TrainQuestionHelper.class));
                break;
            case PARA_VALIDATION_EXCEL:
                errMsgs = tpParaValidationService.insertParaValidation(new BasicTableInfoHelper(), transform(dataList, ParaValidationInfoHelper.class));
                break;
            case PARA_RELATED_CASE_EXCEL:
                errMsgs = tpParaRelatedCaseService.insertParaRelatedCase(new BasicTableInfoHelper(), dataList);
                break;
            default:
                //不支持的导入类型
                throw new ServiceErrorException(MessageUtils.message("excel.message.unsupport"));
        }
        Map<String,Object> result = new HashMap<>();
        if(ObjectUtils.isEmpty(errMsgs)){
            if(getEnum(tableType) == TRAIN_QUESTION_EXCEL){
                result.put("successList",resultList);
            }else {
                result.put("successList",dataList);
            }
            result.put("errorList",new ArrayList<>());
            result.put("message",new ArrayList<>());
        }else {
            result.put("successList",new ArrayList<>());
            result.put("errorList",dataList);
            result.put("message",errMsgs);
        }
        return AjaxResult.success(result);
    }

    /**
     * 对多个表单校验应用校验方法(为了获取错误数据的位置，所以每个sheet单独校验)
     *
     * @param check
     * @param tableInfo
     * @param sheets
     * @param clazz
     * @param <T>
     * @param <U>
     * @return
     */
    private <T, U> UploadResult<U> checkSheets(BiFunction<T, List<U>, UploadResult<U>> check, T tableInfo,
        List<XSSFSheet> sheets, Class<U> clazz) {
        UploadResult<U> result = null;
        for (XSSFSheet sheet : sheets) {
            UploadResult<U> current = check.apply(tableInfo, readData(sheet, clazz))
                .sheet(sheet.getSheetName());
            if (Objects.isNull(result)) {
                result = current;
            } else {
                result.join(current);
            }
        }
        return result;
    }

    private <T, U> UploadResult<U> checkSheetsByList(BiFunction<T, List<U>, UploadResult<U>> check, T tableInfo,
        List<U> list, Class<U> clazz) {
        UploadResult<U> result = null;
        result = check.apply(tableInfo, list);
        return result;
    }

    /**
     * 将hashmap集合转换成对应对象的集合
     *
     * @param data
     * @param clazz
     * @param <T>
     * @return
     */
    private <T> List<T> transform(List<HashMap> data, Class<? extends T> clazz) {
        return data.stream()
            .map(item -> JSON.parseObject(JSON.toJSONString(item), clazz))
            .filter(Objects::nonNull).collect(toList());
    }

    /**
     * 获取基本导入信息并返回相应对象
     *
     * @param sheet
     * @param clazz
     * @param <T>
     * @return
     */
    private <T> T getTableInfo(XSSFSheet sheet, Class<T> clazz) {
        List<T> tableInfoHelperList = readData(sheet, clazz);
        if (CollectionUtils.isEmpty(tableInfoHelperList)) {
            throw new ServiceErrorException(MessageUtils.message("excel.message.rightMB"));
            //throw new ServiceErrorException("无法获取模板基本信息，请使用正确的模板");
        }
        T tableInfo = tableInfoHelperList.get(0);
        return tableInfo;
    }
}
