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

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.TermQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.TermsQuery;
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.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.framework.organ.core.aop.OrganIgnore;
import com.cf.imes.framework.security.core.LoginUser;
import com.cf.imes.framework.security.core.util.SecurityFrameworkUtils;
import com.cf.imes.module.system.controller.admin.machine.vo.*;
import com.cf.imes.module.system.convert.machine.MachineConvert;
import com.cf.imes.module.system.dal.dataobject.machine.DrillSettingDO;
import com.cf.imes.module.system.dal.dataobject.machine.MachineDO;
import com.cf.imes.module.system.dal.dataobject.machine.CuttingSettingDO;
import com.cf.imes.module.system.dal.dataobject.machine.MachineLimitDO;
import com.cf.imes.module.system.dal.dataobject.machinetemplate.MachineTemplateLimitDO;
import com.cf.imes.module.system.dal.mysql.machine.MachineMapper;
import com.cf.imes.module.system.dal.mysql.machine.MachineLimitMapper;
import com.cf.imes.module.system.dal.mysql.machinetemplate.MachineTemplateLimitMapper;
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.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

import com.cf.imes.framework.common.pojo.PageResult;
import com.cf.imes.framework.common.util.object.BeanUtils;

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.framework.security.core.util.SecurityFrameworkUtils.getUserOrganId;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.*;

/**
 * 机台 Service 实现类
 *
 * @author 晨丰科技
 */
@Service
@Slf4j
@Validated
public class MachineServiceImpl implements MachineService {

    @Resource
    private ElasticsearchClient elasticsearchClient;

    @Resource
    private ESDocumentService esDocumentService;
    //开料机台配置索引
    public final static String CUTTING_MACHINE_SETTING = "cutting_machine_setting";

    //钻孔机台配置索引
    public final static String DRILL_MACHINE_SETTING = "drill_machine_setting";

    @Resource
    private MachineMapper machineMapper;

    @Resource
    private MachineLimitMapper machineLimitMapper;

    @Resource
    private MachineTemplateLimitMapper machineTemplateLimitMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createCutting(CuttingSaveReqVO createReqVO) {
        LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
        assert loginUser != null;
        MachineTemplateLimitDO organMachineTemplateDO = machineTemplateLimitMapper.selectOne(new LambdaQueryWrapperX<MachineTemplateLimitDO>()
                .eq(MachineTemplateLimitDO::getMachineId, createReqVO.getTemplateId())
                .eq(MachineTemplateLimitDO::getOrganId, loginUser.getOrganId())
        );
        // 插入
        MachineDO machineDO = MachineConvert.convert(createReqVO);
        machineMapper.insert(machineDO);
        MachineLimitDO machineLimitDO = MachineLimitDO.builder()
                .organId(loginUser.getOrganId())
                .machineId(machineDO.getId())
                .showPriorFacing(Objects.isNull(organMachineTemplateDO)? Boolean.FALSE: organMachineTemplateDO.getShowPriorFacing())
                .showAutoNotePrinter(Objects.isNull(organMachineTemplateDO)? Boolean.FALSE: organMachineTemplateDO.getShowAutoNotePrinter())
                .showDualWorkstation(Objects.isNull(organMachineTemplateDO)? Boolean.FALSE: organMachineTemplateDO.getShowDualWorkstation())
                .build();

        machineLimitMapper.insert(machineLimitDO);
        // 返回
        return machineDO.getId();
    }

    @Override
    public void updateCutting(CuttingSaveReqVO updateReqVO) {
        // 校验存在
        validateExists(updateReqVO.getId());
        // 更新
        MachineDO updateObj = MachineConvert.convert(updateReqVO);
        //MachineDO updateObj = BeanUtils.toBean(updateReqVO, MachineDO.class);
        machineMapper.updateById(updateObj);
    }

    @Override
    public void deleteCutting(Long id) {
        // 校验存在
        MachineDO machineDO = machineMapper.selectById(id);
        if (machineDO == null) {
            throw exception(MACHINE_NOT_EXISTS);
        }
        machineMapper.deleteById(id);
    }

    @Override
    public void batchDeleteCutting(List<Long> ids) {
        List<MachineDO> machineDOS = machineMapper.selectBatchIds(ids);
        if (CollectionUtil.isEmpty(machineDOS)) {
            throw exception(MACHINE_NOT_EXISTS);
        }
        machineMapper.deleteBatchIds(ids);
    }

    @Override
    public CuttingRespVO getCutting(Long id) {
        MachineDO machineDO = machineMapper.selectById(id);
        if (Objects.isNull(machineDO)) {
            throw exception(MACHINE_NOT_EXISTS);
        }
        LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
        assert loginUser != null;
        MachineLimitDO machineLimitDO = machineLimitMapper.selectOne(new LambdaQueryWrapperX<MachineLimitDO>()
                .eq(MachineLimitDO::getMachineId, id)
                .eq(MachineLimitDO::getOrganId, loginUser.getOrganId())
        );
        CuttingRespVO respVO = MachineConvert.convert(machineDO, machineLimitDO);
        return respVO;
    }

    @Override
    public Long createDrill(DrillSaveReqVO createReqVO) {
        // 插入
        MachineDO machineDO = Convert.convert(MachineDO.class, createReqVO);
        machineMapper.insert(machineDO);
        DrillSettingDO machineSettingDO = createReqVO.getMachineSettingDO();
        machineSettingDO.setMachineId(machineDO.getId());
        try {
            esDocumentService.createByFluentDSL(DRILL_MACHINE_SETTING, machineSettingDO.getId(), machineSettingDO);
        } catch (Exception e) {
            log.error(e.getMessage());
            throw exception(INTERNAL_SERVER_ERROR);
        }
        // 返回
        return machineDO.getId();
    }

    @Override
    public void updateDrill(DrillSaveReqVO updateReqVO) {
        // 校验存在
        validateExists(updateReqVO.getId());
        updateReqVO.getMachineSettingDO().setMachineId(updateReqVO.getId());
        try {
            esDocumentService.createByFluentDSL(DRILL_MACHINE_SETTING, updateReqVO.getMachineSettingDO().getId(), updateReqVO.getMachineSettingDO());
        } catch (Exception e) {
            log.error(e.getMessage());
            throw exception(INTERNAL_SERVER_ERROR);
        }
        // 更新
        MachineDO updateObj = BeanUtils.toBean(updateReqVO, MachineDO.class);
        machineMapper.updateById(updateObj);
    }

    @Override
    public void deleteDrill(Long id) {
        // 校验存在
        MachineDO machineDO = machineMapper.selectById(id);
        if (machineMapper.selectById(id) == null) {
            throw exception(MACHINE_NOT_EXISTS);
        }
        // 删除
        DeleteByQueryRequest.Builder builder = new DeleteByQueryRequest.Builder()
                .index(DRILL_MACHINE_SETTING)
                .query(b -> b.term(t -> t.field("machineId").value(machineDO.getId())));
        try {
            elasticsearchClient.deleteByQuery(builder.build());
        } catch (IOException e) {
            log.error(e.getMessage());
            throw exception(INTERNAL_SERVER_ERROR);
        }
        machineMapper.deleteById(id);
    }

    @Override
    public void batchDeleteDrill(List<Long> ids) {
        List<MachineDO> machineDOS = machineMapper.selectBatchIds(ids);
        if (CollectionUtil.isEmpty(machineDOS)) {
            throw exception(MACHINE_NOT_EXISTS);
        }
        List<FieldValue> fieldValues = machineDOS.stream().map(e -> FieldValue.of(e.getId())).collect(Collectors.toList());
        DeleteByQueryRequest.Builder builder = new DeleteByQueryRequest.Builder()
                .index(DRILL_MACHINE_SETTING)
                .query(TermsQuery.of(e -> e.field("machineId").terms(b -> b.value(fieldValues)))._toQuery());
        try {
            elasticsearchClient.deleteByQuery(builder.build());
        } catch (IOException e) {
            log.error(e.getMessage());
            throw exception(INTERNAL_SERVER_ERROR);
        }
        machineMapper.deleteBatchIds(ids);
    }

    @Override
    public DrillRespVO getDrill(Long id) {
        MachineDO machineDO = machineMapper.selectById(id);
        if (Objects.isNull(machineDO)) {
            throw exception(MACHINE_NOT_EXISTS);
        }
        DrillRespVO respVO = Convert.convert(DrillRespVO.class, machineDO);
        Object o1 = buildResp(machineDO.getId(), DRILL_MACHINE_SETTING, DrillSettingDO.class);
        respVO.setMachineSettingDO((DrillSettingDO) o1);
        return respVO;
    }

    @Override
    public List<MachineDO> list(Collection<Long> ids) {
        return machineMapper.selectBatchIds(ids);
    }

    @Override
    public Map<Integer, List<MachineVO>> getMachineTree(Long type) {
        List<MachineDO> machineDOS = machineMapper.selectList(new LambdaQueryWrapperX<MachineDO>()
                .eqIfPresent(MachineDO::getOrganId, getUserOrganId())
                .eqIfPresent(MachineDO::getMachineType, type)
                .select(MachineDO::getName, MachineDO::getMachineType, MachineDO::getId, MachineDO::getLabelId, MachineDO::getCreateTime)
        );
        List<MachineVO> machineVOS = BeanUtils.toBean(machineDOS, MachineVO.class);
        Map<Integer, List<MachineVO>> map = machineVOS.stream().collect(Collectors.groupingBy(MachineVO::getMachineType));
        return map;
    }

    @Override
    public Boolean auth(MachineOrgAuthReq vo) {
        machineLimitMapper.delete(new LambdaQueryWrapperX<MachineLimitDO>().eq(MachineLimitDO::getOrganId, vo.getOrganId()));

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

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

            }
            machineLimitMapper.insertBatch(organMachineDOS);
        }

        return Boolean.TRUE;
    }

    @Override
    public PageResult<OrganMachineResp> organMachinePage(OrganMachinePage page) {
        Page<OrganMachineResp> respPage = new Page<>(page.getPageNo(), page.getPageSize());
        Page<OrganMachineResp> pageRes = machineMapper.selectOrganMachinePage(respPage, page);
        return new PageResult<>(pageRes.getRecords(), pageRes.getTotal());
    }

    @Override
    public OrganMachineResp organMachineByOrganId(Long organId) {
        return machineMapper.selectOrganMachine(organId);
    }

    @Override
    public PageResult<MachineDO> page(MachinePageReqVO pageParam) {
        return machineMapper.selectPage(pageParam, new ArrayList<>());
    }

    @Override
    @OrganIgnore
    public List<MachineSimpleRespVO> simpleList(Long organId,Long type) {
        List<MachineDO> machineDOS = machineMapper.selectList(new LambdaQueryWrapperX<MachineDO>()
                .eqIfPresent(MachineDO::getOrganId, organId)
                .eqIfPresent(MachineDO::getMachineType, type)
                .select(MachineDO::getId, MachineDO::getName, MachineDO::getMachineType)
        );
        return BeanUtils.toBean(machineDOS, MachineSimpleRespVO.class);
    }

    private void validateExists(Long id) {
        if (machineMapper.selectById(id) == null) {
            throw exception(MACHINE_NOT_EXISTS);
        }
    }


    @Override
    public PageResult<MachineDO> getPage(MachinePageReqVO pageReqVO) {
        return machineMapper.selectPage(pageReqVO);
    }


    private Object buildResp(Long machineId, 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("machineId").value(machineId))));
        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 exception(INTERNAL_SERVER_ERROR);
        }
    }

}