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

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.TypeReference;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.FieldValue;
import co.elastic.clients.elasticsearch._types.query_dsl.TermsQuery;
import co.elastic.clients.elasticsearch.core.DeleteByQueryRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.Hit;
import com.cf.imes.framework.common.exception.ServiceException;
import com.cf.imes.framework.es.core.service.ESDocumentService;
import com.cf.imes.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.cf.imes.module.system.dal.dataobject.labelelementproperty.LabelElementPropertyDO;
import com.cf.imes.module.system.dal.dataobject.labeltemplate.LabelElementTemplateDO;
import com.cf.imes.module.system.dal.dataobject.machine.CuttingSettingDO;
import com.cf.imes.module.system.dal.mysql.labeltemplate.LabelElementTemplateMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

import com.cf.imes.module.system.controller.admin.labeltemplate.vo.*;
import com.cf.imes.module.system.dal.dataobject.labeltemplate.LabelTemplateDO;
import com.cf.imes.framework.common.pojo.PageResult;
import com.cf.imes.framework.common.util.object.BeanUtils;
import com.cf.imes.module.system.dal.mysql.labeltemplate.LabelTemplateMapper;

import javax.annotation.Resource;

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.*;

/**
 * 标签模板 Service 实现类
 *
 * @author 晨丰科技
 */
@Service
@Validated
@Slf4j
public class LabelTemplateServiceImpl implements LabelTemplateService {

    @Resource
    private LabelTemplateMapper labelTemplateMapper;
    @Resource
    private LabelElementTemplateMapper labelElementTemplateMapper;
    @Resource
    private ESDocumentService esDocumentService;
    @Resource
    private ElasticsearchClient elasticsearchClient;

    private static final String LABEL_TEMPLATE_ELEMENT_PROPERTY_INX = "label_template_element_property";

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createLabelTemplate(LabelTemplateSaveReqVO createReqVO) {
        // 插入
        LabelTemplateDO labelTemplate = BeanUtils.toBean(createReqVO, LabelTemplateDO.class);
        labelTemplateMapper.insert(labelTemplate);

        // 插入子表
        List<LabelElementTemplateSaveReqVO> createReqVOElements = createReqVO.getElements();
        List<LabelElementPropertyDO> labelElementPropertyDOS = new ArrayList<>();
        createReqVOElements.forEach(e -> {
            e.setLabelTemplateId(labelTemplate.getId());
            LabelElementTemplateDO elementTemplateDO = Convert.convert(LabelElementTemplateDO.class, e);
            labelElementTemplateMapper.insert(elementTemplateDO);
            LabelElementPropertyDO propertyDO = e.getPropertyDO();
            propertyDO.setElementId(elementTemplateDO.getId());
            labelElementPropertyDOS.add(propertyDO);
        });
        //插入索引
        try {
            esDocumentService.bulkCreate(LABEL_TEMPLATE_ELEMENT_PROPERTY_INX, labelElementPropertyDOS);
        } catch (Exception e) {
            log.error(e.getMessage());
            throw exception(INTERNAL_SERVER_ERROR);
        }
        // 返回
        return labelTemplate.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateLabelTemplate(LabelTemplateSaveReqVO updateReqVO) {
        // 校验存在
        validateLabelTemplateExists(updateReqVO.getId());
        // 更新
        LabelTemplateDO updateObj = BeanUtils.toBean(updateReqVO, LabelTemplateDO.class);
        labelTemplateMapper.updateById(updateObj);

        // 更新子表
        List<LabelElementTemplateSaveReqVO> elements = updateReqVO.getElements();
        /*updateLabelElementTemplateList(updateReqVO.getId(), Convert.convert(new TypeReference<List<LabelElementTemplateDO>>() {
        }, elements));*/
        labelElementTemplateMapper.updateBatch(Convert.convert(new TypeReference<List<LabelElementTemplateDO>>() {
        }, elements));
        List<LabelElementPropertyDO> labelElementPropertyDOS = elements.stream().map(e -> e.getPropertyDO()).collect(Collectors.toList());
        try {
            esDocumentService.bulkCreate(LABEL_TEMPLATE_ELEMENT_PROPERTY_INX, labelElementPropertyDOS);
        } catch (Exception e) {
            log.error(e.getMessage());
            throw exception(INTERNAL_SERVER_ERROR);
        }

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteLabelTemplate(Long id) {
        // 校验存在
        LabelTemplateDO labelTemplateDO = labelTemplateMapper.selectById(id);
        if (labelTemplateDO == null) {
            throw exception(LABEL_TEMPLATE_NOT_EXISTS);
        }
        List<LabelElementTemplateDO> labelElementTemplateDOS = labelElementTemplateMapper.selectListByLabelTemplateId(id);
        List<FieldValue> fieldValues = labelElementTemplateDOS.stream().map(e -> FieldValue.of(e.getId())).collect(Collectors.toList());
        // 删除索引
        DeleteByQueryRequest.Builder builder = new DeleteByQueryRequest.Builder()
                .index(LABEL_TEMPLATE_ELEMENT_PROPERTY_INX)
                .query(TermsQuery.of(e -> e.field("elementId").terms(b -> b.value(fieldValues)))._toQuery());
        try {
            elasticsearchClient.deleteByQuery(builder.build());
        } catch (IOException e) {
            log.error(e.getMessage());
            throw exception(INTERNAL_SERVER_ERROR);
        }
        // 删除标签模板表
        labelTemplateMapper.deleteById(id);
        // 删除标签元素模板表
        labelElementTemplateMapper.delete(new LambdaQueryWrapperX<LabelElementTemplateDO>().eq(LabelElementTemplateDO::getLabelTemplateId, id));

    }

    private void validateLabelTemplateExists(Long id) {
        if (labelTemplateMapper.selectById(id) == null) {
            throw exception(LABEL_TEMPLATE_NOT_EXISTS);
        }
    }

    @Override
    public LabelTemplateRespVO getLabelTemplate(Long id) {
        LabelTemplateDO labelTemplateDO = labelTemplateMapper.selectById(id);
        List<LabelElementTemplateDO> labelElementTemplateDOS = labelElementTemplateMapper.selectListByLabelTemplateId(labelTemplateDO.getId());
        LabelTemplateRespVO labelTemplateRespVO = BeanUtils.toBean(labelTemplateDO, LabelTemplateRespVO.class);
        List<LabelElementTemplateRespVO> labelElementTemplateRespVOS = BeanUtils.toBean(labelElementTemplateDOS, LabelElementTemplateRespVO.class);
        List<FieldValue> fieldValues = labelElementTemplateDOS.stream().map(e -> FieldValue.of(e.getId())).collect(Collectors.toList());
        SearchResponse<CuttingSettingDO> searchSetting = null;
        try {
            SearchResponse<LabelElementPropertyDO> search = elasticsearchClient.search(builder -> builder.index(LABEL_TEMPLATE_ELEMENT_PROPERTY_INX)
                            .query(TermsQuery.of(e -> e.field("elementId")
                                    .terms(b -> b.value(fieldValues)))._toQuery())
                            .from(0)
                            .size(10000),
                    LabelElementPropertyDO.class);
            List<Hit<LabelElementPropertyDO>> hits = search.hits().hits();
            if (CollectionUtil.isNotEmpty(hits)) {
                List<LabelElementPropertyDO> propertyDOS = hits.stream().map(e -> e.source()).collect(Collectors.toList());
                for (LabelElementTemplateRespVO respVO : labelElementTemplateRespVOS) {
                    respVO.setPropertyDO(propertyDOS.stream().filter(e -> Objects.equals(e.getElementId(), respVO.getId())).findAny().orElse(null));
                }
            }
        } catch (IOException e) {
            log.error(e.getMessage());
            throw new ServiceException(INTERNAL_SERVER_ERROR);
        }

        labelTemplateRespVO.setLabelElementTemplates(labelElementTemplateRespVOS);
        return labelTemplateRespVO;
    }

    @Override
    public PageResult<LabelTemplateDO> getLabelTemplatePage(LabelTemplatePageReqVO pageReqVO) {
        return labelTemplateMapper.selectPage(pageReqVO);
    }

    // ==================== 子表（标签元素模板） ====================

    @Override
    public List<LabelElementTemplateRespVO> getLabelElementTemplateListByLabelTemplateId(Long labelTemplateId) {
        List<LabelElementTemplateDO> labelElementTemplateDOS = labelElementTemplateMapper.selectListByLabelTemplateId(labelTemplateId);
        List<LabelElementTemplateRespVO> respVOS = BeanUtils.toBean(labelElementTemplateDOS, LabelElementTemplateRespVO.class);
        List<FieldValue> fieldValues = labelElementTemplateDOS.stream().map(e -> FieldValue.of(e.getId())).collect(Collectors.toList());
        try {
            SearchResponse<LabelElementPropertyDO> response = elasticsearchClient.search(b -> b.index(LABEL_TEMPLATE_ELEMENT_PROPERTY_INX)
                            .from(0)
                            .size(10000)
                            .query(TermsQuery.of(e -> e.field("elementId").terms(t -> t.value(fieldValues)))._toQuery()),
                    LabelElementPropertyDO.class);
            List<Hit<LabelElementPropertyDO>> hits = response.hits().hits();
            if(CollectionUtil.isNotEmpty(hits)) {
                List<LabelElementPropertyDO> propertyDOS = hits.stream().map(e -> e.source()).collect(Collectors.toList());
                for (LabelElementTemplateRespVO respVO : respVOS) {
                    respVO.setPropertyDO(propertyDOS.stream().filter(e -> Objects.equals(e.getElementId(), respVO.getId())).findAny().orElse(null));
                }
            }
        } catch (IOException e) {
            log.error(e.getMessage());
            throw exception(INTERNAL_SERVER_ERROR);
        }
        return respVOS;
    }

    @Override
    public Map<String, List<LabelTemplateRespVO>> getGroupList() {
        List<LabelTemplateDO> labelTemplateDOS = labelTemplateMapper.selectList();
        List<LabelTemplateRespVO> labelTemplateRespVOS = BeanUtils.toBean(labelTemplateDOS, LabelTemplateRespVO.class);
        Set<Long> ids = labelTemplateRespVOS.stream().map(e -> e.getId()).collect(Collectors.toSet());
        List<LabelElementTemplateDO> labelElementTemplateDOS = labelElementTemplateMapper.selectList(new LambdaQueryWrapperX<LabelElementTemplateDO>().in(LabelElementTemplateDO::getLabelTemplateId, ids));
        List<LabelElementTemplateRespVO> labelElementTemplateRespVOS = BeanUtils.toBean(labelElementTemplateDOS, LabelElementTemplateRespVO.class);
        List<FieldValue> fieldValues = labelElementTemplateRespVOS.stream().map(e -> FieldValue.of(e.getId())).collect(Collectors.toList());
        try {
            SearchResponse<LabelElementPropertyDO> search = elasticsearchClient.search(builder -> builder.index(LABEL_TEMPLATE_ELEMENT_PROPERTY_INX)
                            .query(TermsQuery.of(e -> e.field("elementId")
                                    .terms(b -> b.value(fieldValues)))._toQuery())
                            .from(0)
                            .size(10000),
                    LabelElementPropertyDO.class);
            List<Hit<LabelElementPropertyDO>> hits = search.hits().hits();
            if (CollectionUtil.isNotEmpty(hits)) {
                List<LabelElementPropertyDO> propertyDOS = hits.stream().map(e -> e.source()).collect(Collectors.toList());
                for (LabelElementTemplateRespVO respVO : labelElementTemplateRespVOS) {
                    respVO.setPropertyDO(propertyDOS.stream().filter(e -> Objects.equals(e.getElementId(), respVO.getId())).findAny().orElse(null));
                }
            }

        } catch (IOException e) {
            log.error(e.getMessage());
            throw new ServiceException(INTERNAL_SERVER_ERROR);
        }
        for (LabelTemplateRespVO labelTemplateRespVO : labelTemplateRespVOS) {
            labelTemplateRespVO.setLabelElementTemplates(labelElementTemplateRespVOS.stream().filter(e -> Objects.equals(e.getLabelTemplateId(), labelTemplateRespVO.getId())).toList());
        }
        return labelTemplateRespVOS.stream().collect(Collectors.groupingBy(e -> e.getType()));
    }

    private void createLabelElementTemplateList(Long labelTemplateId, List<LabelElementTemplateDO> list) {
        list.forEach(o -> o.setLabelTemplateId(labelTemplateId));
        labelElementTemplateMapper.insertBatch(list);
    }

    private void updateLabelElementTemplateList(Long labelTemplateId, List<LabelElementTemplateDO> list) {
        deleteLabelElementTemplateByLabelTemplateId(labelTemplateId);
        list.forEach(o -> o.setId(null).setUpdater(null).setUpdateTime(null)); // 解决更新情况下：1）id 冲突；2）updateTime 不更新
        createLabelElementTemplateList(labelTemplateId, list);
    }

    private void deleteLabelElementTemplateByLabelTemplateId(Long labelTemplateId) {
        labelElementTemplateMapper.deleteByLabelTemplateId(labelTemplateId);
    }

}