package org.dromara.service.impl;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import jakarta.annotation.PostConstruct;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import org.dromara.config.CamelRouterConfig;
import org.dromara.utils.ScriptCache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.dromara.domain.bo.TblRulesBo;
import org.dromara.domain.vo.TblRulesVo;
import org.dromara.domain.TblRules;
import org.dromara.mapper.TblRulesMapper;
import org.dromara.service.ITblRulesService;

import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

/**
 * 规则Service业务层处理
 *
 * @author Lion Li
 * @date 2024-08-14
 */
@RequiredArgsConstructor
@Service
public class TblRulesServiceImpl implements ITblRulesService {

    private static final Logger log = LoggerFactory.getLogger(TblRulesServiceImpl.class);
    private final TblRulesMapper baseMapper;

    @Autowired
    private CamelRouterConfig camelRouterConfig;

    /**
     * 查询规则
     *
     * @param id 主键
     * @return 规则
     */
    @Override
    public TblRulesVo queryById(Long id){
        TblRulesVo vo =      baseMapper.selectVoById(id);
        if(vo.getExt1()!=null){
            vo.setExt1(Base64.encode(vo.getExt1()));
        }
        return vo;
    }

    /**
     * 分页查询规则列表
     *
     * @param bo        查询条件
     * @param pageQuery 分页参数
     * @return 规则分页列表
     */
    @Override
    public TableDataInfo<TblRulesVo> queryPageList(TblRulesBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<TblRules> lqw = buildQueryWrapper(bo);
        Page<TblRulesVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }

    /**
     * 查询符合条件的规则列表
     *
     * @param bo 查询条件
     * @return 规则列表
     */
    @Override
    public List<TblRulesVo> queryList(TblRulesBo bo) {
        LambdaQueryWrapper<TblRules> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    private LambdaQueryWrapper<TblRules> buildQueryWrapper(TblRulesBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<TblRules> lqw = Wrappers.lambdaQuery();
        lqw.like(StringUtils.isNotBlank(bo.getName()), TblRules::getName, bo.getName());
        lqw.eq(StringUtils.isNotBlank(bo.getRoutes()), TblRules::getRoutes, bo.getRoutes());
        lqw.eq(StringUtils.isNotBlank(bo.getRemark()), TblRules::getRemark, bo.getRemark());
        lqw.eq(StringUtils.isNotBlank(bo.getExt1()), TblRules::getExt1, bo.getExt1());
        lqw.eq(StringUtils.isNotBlank(bo.getExt2()), TblRules::getExt2, bo.getExt2());
        lqw.eq(StringUtils.isNotBlank(bo.getStatus()), TblRules::getStatus, bo.getStatus());
        return lqw;
    }

    /**
     * 新增规则
     *
     * @param bo 规则
     * @return 是否新增成功
     */
    @Override
    public Boolean insertByBo(TblRulesBo bo) {
        TblRules add = MapstructUtils.convert(bo, TblRules.class);
        validEntityBeforeSave(add);
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setId(add.getId());
        }
        return flag;
    }

    /**
     * 修改规则
     *
     * @param bo 规则
     * @return 是否修改成功
     */
    @Override
    public Boolean updateByBo(TblRulesBo bo) throws Exception {
        TblRules update = MapstructUtils.convert(bo, TblRules.class);
        validEntityBeforeSave(update);

        if(update.getExt1()!=null) {
            update.setExt1(Base64.decodeStr(update.getExt1()));
            update.setRoutes(covetToUIToRouter(update.getExt1()));
        }
        if(update.getStatus()!=null){
           TblRulesVo rulesVo =  baseMapper.selectVoById(update.getId());
            if(update.getStatus().equals("1")){
                //启用
                camelRouterConfig.camelRouterRemove(rulesVo.getId().toString());
                if(camelRouterConfig.camelRouterRun(covertToRouteXml(rulesVo))){
                    //路由xml转换成功

                }else{
                    throw new Exception("路由xml转换失败");
                }
            }
            if(update.getStatus().equals("0")){
                //停用
                if(camelRouterConfig.camelRouterRemove(rulesVo.getId().toString())){
                    //路由xml转换成功
                }else {
                    throw new Exception("路由xml转换失败");
                }
            }
        }
        return baseMapper.updateById(update) > 0;
    }

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(TblRules entity){
        //TODO 做一些数据校验,如唯一约束
    }

    /**
     * 校验并批量删除规则信息
     *
     * @param ids     待删除的主键集合
     * @param isValid 是否进行有效性校验
     * @return 是否删除成功
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if(isValid){
            //TODO 做一些业务上的校验,判断是否需要校验
        }
        return baseMapper.deleteByIds(ids) > 0;
    }

    private String covetToUIToRouter(String  uicell) throws Exception {
        //分析并保存成路由xml
        Map<String,Object> routeMap = new HashMap<>();
        JSONArray cells = JSONUtil.parseObj(uicell).getJSONArray("cells");
        if (cells != null) {
            Map<String, JSONObject> hasMap = cells.stream().collect(Collectors.toMap(item -> JSONUtil.parseObj(item).getStr("id"), item -> JSONUtil.parseObj(item)));
            //转换routejson
            //查找连接线
            List<Object> lines = cells.stream().filter(item -> JSONUtil.parseObj(item).getStr("shape").equals("edge")).collect(Collectors.toList());

            List<String> tagers = new ArrayList<>();
            lines.stream().forEach(item->{
                JSONObject linet = (JSONObject) item;
                tagers.add(linet.getJSONObject("target").getStr("cell"));
            });
            List<String> fromids = new ArrayList<>();
            for (String id :hasMap.keySet()) {
                if(!tagers.contains(id)){
                    if(!hasMap.get(id).getStr("shape").equals("edge")){
                        fromids.add(id);
                    }
                }
            }
            if(fromids.size()<1){
                throw new Exception("未找到起始节点");
            }
            if(fromids.size()>1){
                throw new Exception("起始节点不能有多个");
            }

            JSONObject fromdata = hasMap.get(fromids.get(0));
            JSONObject fromonj = fromdata.getJSONObject("data").getJSONObject("data");
            String from = fromonj.getStr("res");
            if(StrUtil.isEmptyIfStr(from)) throw new Exception("未找到起始节点");
            from = replacedata(from, fromonj);
            List<Map<String,Object>> routes = new ArrayList<>();
            routeMap = getnextNode(0,routes,fromdata,lines,hasMap);
            routeMap.put("from", from);
            routes.add(routeMap);

            return JSONUtil.toJsonStr(routes);
        }
        return null;
    }
    private Map<String,Object> getnextNode(int indexp,List<Map<String,Object>> routes, JSONObject from,List<Object> lines,Map<String,JSONObject> allnode){
        //先找线
        Map<String,Object> tolist = new HashMap<>();
        AtomicInteger index = new AtomicInteger(indexp);
        List<String> targetids = new ArrayList<>();
        AtomicReference<String> targetid = new AtomicReference<>("");
        lines.stream().forEach(item->{
            JSONObject linet = (JSONObject) item;
            if(linet.getJSONObject("source").getStr("cell").equals(from.getStr("id"))){
                targetids.add(linet.getJSONObject("target").getStr("cell"));
                //目标线段
                JSONObject linedata = linet.getJSONObject("data").getJSONObject("configdom").getJSONObject("data");
                String lineto = linedata.getStr("res");
                //线不做定义流程 跳过
                if((!StrUtil.isEmptyIfStr(lineto))&&false){
                    lineto = replacedata(lineto, linedata);
                    tolist.put(StrUtil.format("to{}", index.getAndIncrement()), lineto);
                }
            }
        });


        for (String tid : targetids) {
            if(!StrUtil.isEmptyIfStr(tid)){
                JSONObject targetnode = allnode.get(tid);
                JSONObject linedata = targetnode.getJSONObject("data").getJSONObject("data");
                String targetstr = linedata.getStr("res");
                if(!StrUtil.isEmptyIfStr(targetstr)){
                    targetstr = replacedata(targetstr, linedata);
                    //根据线找目标节点
                    if(targetids.size()>1){
                        //多节点需要采用 multicast
                        if(tolist.get("multicast")==null){
                            Map<String,Object> map = new HashMap<>();
                            tolist.put("multicast",map);
                        }
                        //此处需要判断子节点
                        Map<String,Object> c = getnextNode(1,routes,targetnode,lines,allnode);
                        if(c.get("multicast")!=null){
                            String UUid = UUID.fastUUID().toString();
                            c.put("from","direct:"+ UUid );
                            c.put(StrUtil.format("to{}", 0),targetstr);
                            routes.add(c);
                            ((Map<String,Object>)tolist.get("multicast")).put(StrUtil.format("to{}", index.getAndIncrement()), "direct:"+ UUid);
                        }else{
                            if(c.get("to1")!=null){
                                String UUid = UUID.fastUUID().toString();
                                c.put("from","direct:"+ UUid );
                                c.put(StrUtil.format("to{}", 0),targetstr);
                                routes.add(c);
                                ((Map<String,Object>)tolist.get("multicast")).put(StrUtil.format("to{}", index.getAndIncrement()), "direct:"+ UUid);
                            }else{
                                ((Map<String,Object>)tolist.get("multicast")).put(StrUtil.format("to{}", index.getAndIncrement()), targetstr);
                            }

                        }
                    }else{
                        Map<String,Object> c = getnextNode(1,routes,targetnode,lines,allnode);
                        if(c.get("multicast")!=null){
                            String UUid = UUID.fastUUID().toString();
                            c.put("from","direct:"+ UUid );
                            c.put(StrUtil.format("to{}", 0),targetstr);
                            routes.add(c);
                            tolist.put(StrUtil.format("to{}", index.getAndIncrement()), "direct:"+ UUid);
                        }else{
                            if(c.get("to1")!=null){
                                String UUid = UUID.fastUUID().toString();
                                c.put("from","direct:"+ UUid );
                                c.put(StrUtil.format("to{}", 0),targetstr);
                                routes.add(c);
                                tolist.put(StrUtil.format("to{}", index.getAndIncrement()), "direct:"+ UUid);
                            }else{
                                tolist.put(StrUtil.format("to{}", index.getAndIncrement()), targetstr);
                            }
                        }
                    }
                }

            }
        }
        return tolist;

    }

    private String replacedata(String data,JSONObject jdata){
        String rdata = data;
        {
            //公共参数修改
            rdata = rdata.replaceAll("\\$\\{uuid}",UUID.fastUUID().toString());


        }
        {
            //私有参数映射
            for (String key : jdata.keySet()) {
                if(key.contains("defscript")){
                    String sc = jdata.getStr(key);
                    for (String key1 : jdata.keySet()) {
                        String tt= jdata.get(key1).toString();
                        if(StrUtil.isEmptyIfStr(jdata.get(key1).toString())){
                            tt = "\\\"\\\"";
                        }
                        sc = sc.replaceAll("\\$\\{"+key1+"}",tt);
                    }
                    jdata.set(key, sc);
                }
                if(rdata.contains("runScript")&& rdata.contains("${"+key+"}")){
                    continue;
                }
                String tt= jdata.get(key).toString();
                if(StrUtil.isEmptyIfStr(jdata.get(key).toString())){
                    tt = "\\\"\\\"";
                }
                rdata = rdata.replaceAll("\\$\\{"+key+"}",tt);
            }

            for (String key : jdata.keySet()) {
                if(rdata.contains("runScript") && rdata.contains("${"+key+"}")){
                    String uuid = UUID.fastUUID().toString().replaceAll("_","");
                    ScriptCache.scriptCache.put(uuid,jdata.get(key).toString());
                    rdata = rdata.replaceAll("\\$\\{"+key+"}",uuid);
                }
            }
        }

        return rdata;
    }

    private String jsontoxml(JSONObject json){
        int index = 0;
        String xml = "";
        if(json.getStr("from")!=null){
            xml+= "<from id=\""+UUID.fastUUID().toString()+"\" uri=\""+json.getStr("from").toString()+"\"></from>";
        }
        if(json.getStr("to" + index)==null){
            index++;
        }

        while (json.getStr("to" + index)!=null){
            if(json.getStr("to" + index).toString().contains("runScript")){
                String[] strs = json.getStr("to" + index).toString().split("_");
                if(strs.length==3 && strs[1].equals("1")){
                    //groovy
                    xml+= "<to  id=\""+UUID.fastUUID().toString()+"\"  uri=\"bean:groovyMessageProcess??method=runScript("+strs[2]+")\"></to>";
                }else{
                    xml+= "";
                }
            }else{
                xml+="<to  id=\""+UUID.fastUUID().toString()+"\" uri=\""+json.getStr("to" + index).toString()+"\"></to>";
            }
            index++;
        }
        if(json.getJSONObject("multicast")!=null){
            xml+= "<multicast>"+ jsontoxml(json.getJSONObject("multicast"))+"</multicast>";
        }
        return xml;
    }

    private String covertToRouteXml(TblRulesVo rules) throws Exception {
        if(StrUtil.isEmptyIfStr(rules.getRoutes())){
            throw new Exception("规则未配置！");
        }
        if(rules.getRoutes().contains("runScript")){
            rules.setRoutes(covetToUIToRouter(rules.getExt1()));
        }
        JSONArray dataj =  JSONUtil.parseArray( rules.getRoutes());
        String xmlroutes = "";
        for (Object jsonObject : dataj) {
            xmlroutes+="<route id=\""+rules.getId()+"__"+UUID.fastUUID().toString()+"\">\n";
            xmlroutes += jsontoxml((JSONObject)jsonObject);
            xmlroutes+="\n</route>";

        }

        String xmlstring = "<routes xmlns=\"http://camel.apache.org/schema/spring\"> " +
                xmlroutes+
                " </routes>";

        return xmlstring;
    }


    @PostConstruct
    public void runAll()
    {
        List<TblRulesVo> lists = baseMapper.selectVoList();
        for (TblRulesVo vo : lists) {
            if(vo.getStatus().equals("1")){
                //启用
                try {
                    if(camelRouterConfig.camelRouterRun(covertToRouteXml(vo))){
                    //路由xml转换成功
                        log.debug("系统初始化启动规则成功");
                    };

                }catch (Exception e){

                }

            }

        }
    }
}
