package com.cf.imes.module.system.service.machine;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.Convert;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.FieldValue;
import co.elastic.clients.elasticsearch._types.query_dsl.*;
import co.elastic.clients.elasticsearch.core.DeleteByQueryRequest;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.Hit;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.cf.imes.framework.common.exception.ServiceException;
import com.cf.imes.framework.common.pojo.PageResult;
import com.cf.imes.framework.es.core.dal.ESDocument;
import com.cf.imes.framework.es.core.service.ESDocumentService;
import com.cf.imes.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.cf.imes.module.system.controller.admin.machine.vo.*;
import com.cf.imes.module.system.convert.machine.MachineTemplateConvert;
import com.cf.imes.module.system.dal.dataobject.machine.CuttingSettingDO;
import com.cf.imes.module.system.dal.dataobject.machine.UserMachineDO;
import com.cf.imes.module.system.dal.dataobject.machinetemplate.DrillTemplateMachineDO;
import com.cf.imes.module.system.dal.dataobject.machinetemplate.MachineTemplateDO;
import com.cf.imes.module.system.dal.dataobject.machinetemplate.MachineTemplateLimitDO;
import com.cf.imes.module.system.dal.mysql.machine.UserMachineMapper;
import com.cf.imes.module.system.dal.mysql.machinetemplate.MachineTemplateMapper;
import com.cf.imes.module.system.dal.mysql.machinetemplate.MachineTemplateLimitMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

import static com.cf.imes.framework.common.exception.enums.GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR;
import static com.cf.imes.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.*;

/**
 * @author there
 */
@Service
@Slf4j
public class MachineTemplateServiceImpl implements MachineTemplateService {

    //开料机台模板配置索引
    private static final String CUTTING_MACHINE_INX = "cutting_machine_template";
    //钻孔机台模板配置索引
    private static final String DRILL_MACHINE_INX = "drill_machine_template";

    @Resource
    private ESDocumentService esDocumentService;

    @Resource
    private UserMachineMapper userMachineMapper;

    @Resource
    private ElasticsearchClient elasticsearchClient;

    @Resource
    private MachineTemplateMapper machineTemplateMapper;

    @Resource
    private MachineTemplateLimitMapper machineTemplateLimitMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createDrillTemplate(DrillTemplateSaveReqVO reqVO) {
        MachineTemplateDO templateDO = Convert.convert(MachineTemplateDO.class, reqVO);
        machineTemplateMapper.insert(templateDO);
        DrillTemplateMachineDO setting = reqVO.getSetting();
        setting.setTemplateId(templateDO.getId());
        try {
            esDocumentService.createByFluentDSL(DRILL_MACHINE_INX, setting.getId(), setting);
        } catch (Exception e) {
            log.error(e.getMessage());
            throw new ServiceException(INTERNAL_SERVER_ERROR);
        }
        return templateDO.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateDrillTemplate(DrillTemplateSaveReqVO reqVO) {
        MachineTemplateDO templateDO = Convert.convert(MachineTemplateDO.class, reqVO);
        machineTemplateMapper.updateById(templateDO);
        DrillTemplateMachineDO machineDO = reqVO.getSetting();
        Map<String, Object> map = BeanUtil.beanToMap(machineDO);
        try {
            esDocumentService.updateById(DRILL_MACHINE_INX, machineDO.getId(), DrillTemplateMachineDO.class, map);
        } catch (IOException e) {
            log.error(e.getMessage());
            throw new ServiceException(INTERNAL_SERVER_ERROR);
        }
        return Boolean.TRUE;
    }

    @Override
    public DrillTemplateRespVO getDirllTemplateById(Long id) {
        MachineTemplateDO templateDO = machineTemplateMapper.selectById(id);
        if (Objects.isNull(templateDO)) {
            throw exception(MACHINE_TEMPLATE_NOT_EXISTS);
        }
        DrillTemplateRespVO respVO = Convert.convert(DrillTemplateRespVO.class, templateDO);
        Object o = buildResp(templateDO.getId(), DRILL_MACHINE_INX, DrillTemplateMachineDO.class);
        respVO.setDrillTemplateMachineDO((DrillTemplateMachineDO) o);
        return respVO;
    }

    @Override
    public Boolean deleteDrillTemplate(Long id) {
        MachineTemplateDO templateDO = machineTemplateMapper.selectById(id);
        if (Objects.isNull(templateDO)) {
            throw exception(MACHINE_TEMPLATE_NOT_EXISTS);
        }
        // 删除
        DeleteByQueryRequest.Builder builder = new DeleteByQueryRequest.Builder()
                .index(DRILL_MACHINE_INX)
                .query(b -> b.term(t -> t.field("templateId").value(templateDO.getId())));
        try {
            elasticsearchClient.deleteByQuery(builder.build());
        } catch (IOException e) {
            log.error(e.getMessage());
            throw new ServiceException(INTERNAL_SERVER_ERROR);
        }
        machineTemplateMapper.deleteById(id);
        return Boolean.TRUE;
    }

    @Override
    public Boolean batchDeleteDrillTemplate(List<Long> ids) {
        List<MachineTemplateDO> machineTemplateDOS = machineTemplateMapper.selectBatchIds(ids);
        if (CollectionUtil.isEmpty(machineTemplateDOS)) {
            throw exception(MACHINE_TEMPLATE_NOT_EXISTS);
        }

        List<FieldValue> fieldValues = machineTemplateDOS.stream().map(e -> FieldValue.of(e.getId())).collect(Collectors.toList());
        DeleteByQueryRequest.Builder builder = new DeleteByQueryRequest.Builder()
                .index(DRILL_MACHINE_INX)
                .query(TermsQuery.of(e -> e.field("templateId").terms(b -> b.value(fieldValues)))._toQuery());
        try {
            elasticsearchClient.deleteByQuery(builder.build());
        } catch (IOException e) {
            log.error(e.getMessage());
            throw new ServiceException(INTERNAL_SERVER_ERROR);
        }
        machineTemplateMapper.deleteBatchIds(ids);
        return Boolean.TRUE;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createCuttingTemplate(CuttingTemplateSaveReqVO reqVO) {
        MachineTemplateDO templateDO = MachineTemplateConvert.convert(reqVO);
        machineTemplateMapper.insert(templateDO);
        return templateDO.getId();
    }

    @Override
    public Boolean updateCuttingTemplate(CuttingTemplateSaveReqVO reqVO) {
        MachineTemplateDO templateDO = MachineTemplateConvert.convert(reqVO);
        machineTemplateMapper.updateById(templateDO);
        return Boolean.TRUE;
    }

    @Override
    public CuttingTemplateRespVO getCuttingTemplateById(String id) {
        MachineTemplateDO templateDO = machineTemplateMapper.selectById(id);
        if (Objects.isNull(templateDO)) {
            throw exception(MACHINE_TEMPLATE_NOT_EXISTS);
        }
        CuttingTemplateRespVO respVO  = MachineTemplateConvert.convert1(templateDO);
        return respVO;
    }

    @Override
    public Boolean batchDeleteCuttingTemplate(List<Long> ids) {
        List<MachineTemplateDO> machineTemplateDOS = machineTemplateMapper.selectBatchIds(ids);
        if (CollectionUtil.isEmpty(machineTemplateDOS)) {
            throw exception(MACHINE_TEMPLATE_NOT_EXISTS);
        }
        machineTemplateMapper.deleteBatchIds(ids);
        return Boolean.TRUE;
    }

    @Override
    public CuttingTemplateRespVO getDefaultCutting(Integer machineType) {
        List<MachineTemplateDO> machineTemplateDOS = machineTemplateMapper.selectList(new LambdaQueryWrapperX<MachineTemplateDO>()
                .eq(MachineTemplateDO::getIsDefault, Boolean.TRUE)
                .eq(MachineTemplateDO::getMachineType, machineType)
        );
        if (CollectionUtil.isNotEmpty(machineTemplateDOS)) {
            if (machineTemplateDOS.size() > 1) {
                throw exception(DEFAULT_TEMPLATE_COUNT);
            }
            MachineTemplateDO templateDO = machineTemplateDOS.get(0);
            return MachineTemplateConvert.convert1(templateDO);
        }
        throw exception(DEFAULT_TEMPLATE_NOT_EXISTS);
    }

    @Override
    public DrillTemplateRespVO getDefaultDrill() {
        List<MachineTemplateDO> machineTemplateDOS = machineTemplateMapper.selectList(new LambdaQueryWrapperX<MachineTemplateDO>()
                .eq(MachineTemplateDO::getIsDefault, Boolean.TRUE)
                .eq(MachineTemplateDO::getMachineType, 2)
        );
        if (CollectionUtil.isNotEmpty(machineTemplateDOS)) {
            if (machineTemplateDOS.size() > 1) {
                throw exception(DEFAULT_TEMPLATE_COUNT);
            }
            MachineTemplateDO templateDO = machineTemplateDOS.get(0);
            DrillTemplateRespVO respVO = Convert.convert(DrillTemplateRespVO.class, templateDO);
            Object o = buildResp(templateDO.getId(), DRILL_MACHINE_INX, DrillTemplateMachineDO.class);
            respVO.setDrillTemplateMachineDO((DrillTemplateMachineDO) o);
            return respVO;
        }
        throw exception(DEFAULT_TEMPLATE_COUNT);
    }

    @Override
    public Map<Integer, List<MachineTemplateDO>> getMachineTemplateTree() {
        List<MachineTemplateDO> machineTemplateDOS = machineTemplateMapper.selectList();
        Map<Integer, List<MachineTemplateDO>> map = machineTemplateDOS.stream().collect(Collectors.groupingBy(MachineTemplateDO::getMachineType));
        return map;
    }

    @Override
    public PageResult<AuthTemplateResp> authTemplatePage(AuthTemplatePage page) {
        Page<AuthTemplateResp> respPage = new Page<>(page.getPageNo(), page.getPageSize());
        Page<AuthTemplateResp> pageRes = machineTemplateMapper.selectAuthTemplatePage(respPage, page);
        return new PageResult<>(pageRes.getRecords(), pageRes.getTotal());
    }

    @Override
    public PageResult<OrganTemplateResp> organTemplatePage(OrganTemplatePage page) {
        Page<OrganTemplateResp> respPage = new Page<>(page.getPageNo(), page.getPageSize());
        Page<OrganTemplateResp> pageRes = machineTemplateMapper.selectOrganTemplatePage(respPage, page);
        return new PageResult<>(pageRes.getRecords(), pageRes.getTotal());
    }

    @Override
    public OrganTemplateResp organTemplateByOrganId(Long organId) {
        return machineTemplateMapper.selectOrganTemplate(organId);
    }

    @Override
    @Transactional
    public Boolean setDefaultTemplate(Long id) {
        MachineTemplateDO templateDO = machineTemplateMapper.selectById(id);
        if (Objects.isNull(templateDO)) {
            throw exception(MACHINE_TEMPLATE_NOT_EXISTS);
        }
        List<MachineTemplateDO> machineTemplateDOS = machineTemplateMapper.selectList(MachineTemplateDO::getMachineType, templateDO.getMachineType(), MachineTemplateDO::getIsDefault, Boolean.TRUE);
        if(CollectionUtil.isEmpty(machineTemplateDOS) || machineTemplateDOS.size()>1) {
            throw exception(DEFAULT_TEMPLATE_COUNT);
        }
        MachineTemplateDO machineTemplateDO = new MachineTemplateDO();
        machineTemplateDO.setId(machineTemplateDOS.get(0).getId());
        machineTemplateDO.setIsDefault(Boolean.FALSE);
        machineTemplateMapper.updateById(machineTemplateDO);

        templateDO.setIsDefault(Boolean.TRUE);
        machineTemplateMapper.updateById(templateDO);
        return Boolean.TRUE;
    }


    @Override
    public Boolean deleteCuttingTemplate(Long id) {
        MachineTemplateDO templateDO = machineTemplateMapper.selectById(id);
        if (Objects.isNull(templateDO)) {
            throw exception(MACHINE_TEMPLATE_NOT_EXISTS);
        }
        if(templateDO.getIsDefault()) {
            throw exception(DEFAULT_TEMPLATE_NOT_DELETED);
        }
        machineTemplateMapper.deleteById(id);
        return Boolean.TRUE;
    }

    private Object buildResp(Long machineTemplateId, String index, Class<? extends ESDocument> clazz) {
        SearchRequest.Builder builder = new SearchRequest.Builder();
        builder.index(index);
        builder.query(q -> q.term(TermQuery.of(e -> e.field("templateId").value(machineTemplateId))));
        SearchResponse<CuttingSettingDO> searchSetting = null;
        try {
            SearchResponse<?> search = elasticsearchClient.search(builder.build(), clazz);
            List<? extends Hit<?>> hits = search.hits().hits();
            if (CollectionUtil.isNotEmpty(hits)) {
                Hit<?> hit = hits.get(0);
                return hit.source();
            }
            return null;
        } catch (IOException e) {
            log.error(e.getMessage());
            throw new ServiceException(INTERNAL_SERVER_ERROR);
        }
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean auth(MachineAuthVO vo) {
        userMachineMapper.delete(new LambdaQueryWrapperX<UserMachineDO>().eq(UserMachineDO::getUserId, vo.getUserId()));
        if (CollectionUtil.isNotEmpty(vo.getMachinesIds())) {
            ArrayList<UserMachineDO> userMachineDOS = new ArrayList<>();
            for (Long machinesId : vo.getMachinesIds()) {
                userMachineDOS.add(UserMachineDO.builder()
                        .userId(vo.getUserId())
                        .machineId(machinesId)
                        .build());
            }
            userMachineMapper.insertBatch(userMachineDOS);
        }
        return Boolean.TRUE;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean auth(MachineOrgAuthReq vo) {
        machineTemplateLimitMapper.delete(new LambdaQueryWrapperX<MachineTemplateLimitDO>().eq(MachineTemplateLimitDO::getOrganId, vo.getOrganId()));

        List<MachineAuth> machineAuthList = vo.getMachineAuthList();

        if(CollectionUtil.isNotEmpty(machineAuthList)) {
            List<MachineTemplateLimitDO> organMachineDOS = new ArrayList<>();
            LocalDateTime now = LocalDateTime.now();
            for (MachineAuth machineAuth : machineAuthList) {
                organMachineDOS.add(
                        MachineTemplateLimitDO.builder()
                                .createTime(now)
                                .organId(vo.getOrganId())
                                .machineId(machineAuth.getMachineId())
                                .showDualWorkstation(machineAuth.getShowDualWorkstation())
                                .showAutoNotePrinter(machineAuth.getShowAutoNotePrinter())
                                .showPriorFacing(machineAuth.getShowPriorFacing())
                                .build()
                );

            }
            machineTemplateLimitMapper.insertBatch(organMachineDOS);
        }

        return Boolean.TRUE;
    }


 /*   @Override
    public PageResult<CuttingMachineDO> pageCutting(PageParam pageParam) {
        LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
        List<String> machineIds = userMachineMapper.selectList(new LambdaQueryWrapperX<UserMachineDO>().eq(UserMachineDO::getUserId, 1L*//*loginUser.getId()*//*))
                .stream()
                .map(e -> e.getMachineId())
                .collect(Collectors.toList());
        List<CuttingMachineDO> cuttingMachineDOS = new ArrayList<>();
        List<Query> queryList = new ArrayList<>();
        List<FieldValue> fieldValues = new ArrayList<>();
        for (String machineId : machineIds) {
            fieldValues.add(FieldValue.of(machineId));
        }
        queryList.add(TermsQuery.of(e-> e.field("_id").terms(b->b.value(fieldValues)))._toQuery());
        queryList.add(TermQuery.of(e->e.field("organId").value( loginUser.getOrganId()))._toQuery());
        SearchResponse<CuttingMachineDO> response = null;
        try {
            response = elasticsearchClient.search(s-> s.index(CUTTING_MACHINE_INX)
                            .from((pageParam.getPageNo() - 1) * pageParam.getPageSize())
                            .size(pageParam.getPageSize())
                            .query(b -> b.bool( builder -> builder.must(queryList))),
                    CuttingMachineDO.class);
        } catch (IOException e) {
            log.error(e.getMessage());
            throw new ServiceException(500, "服务器异常");
        }
        long value = response.hits().total().value();
        for (Hit<CuttingMachineDO> hit : response.hits().hits()) {
            cuttingMachineDOS.add(hit.source());
        }
        return new PageResult<CuttingMachineDO>(cuttingMachineDOS, value);
    }*/

    @Override
    public PageResult<MachineTemplateDO> page(MachinePageReqVO pageParam) {
       /* LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
        List<Long> machineIds = userMachineMapper.selectList(new LambdaQueryWrapperX<UserMachineDO>().eq(UserMachineDO::getUserId, loginUser.getId()))
                .stream()
                .map(e -> e.getMachineId())
                .collect(Collectors.toList());
        if(CollectionUtil.isEmpty(machineIds)) {
            return new PageResult<>();
        }*/
        return machineTemplateMapper.selectPage(pageParam, new ArrayList<>());
    }


}
