package cc.smtweb.system.bpm.web.sys.user.dataRightGroup.impl;

import cc.smtweb.framework.core.common.SwConsts;
import cc.smtweb.framework.core.common.SwMap;
import cc.smtweb.framework.core.db.EntityHelper;
import cc.smtweb.framework.core.util.CommUtil;
import cc.smtweb.framework.core.util.StringUtil;
import cc.smtweb.system.bpm.web.common.BpmConst;
import cc.smtweb.system.bpm.web.common.BpmEnum;
import cc.smtweb.system.bpm.web.sys.user.dataRightGroup.AbsTreeDataRightHandler;
import cc.smtweb.system.bpm.web.sys.user.dataRightGroup.DataRightGroupItem;
import cc.smtweb.system.bpm.web.sys.user.party.Party;
import cc.smtweb.system.bpm.web.sys.user.party.PartyCache;

import java.util.HashSet;
import java.util.Random;
import java.util.Set;

/**
 * @Author: tanghp
 * @Date: 2022-09-22 15:17
 * @Desc: 单位数据权限
 */
public class PartyDataRightHandler extends AbsTreeDataRightHandler {
    @Override
    protected String getCurValue() {
        long partyId = us.getPartyId();
        return partyId<=0L? "": String.valueOf(partyId);
    }

    @Override
    protected String getDataRightType() {
        return BpmEnum.DataRightType.PARTY.value;
    }

    @Override
    public void buildSqlWhere(String sqlField, String value, StringBuilder sql, SwMap args) {
        buildSqlWhere(sqlField, StringUtil.isEmpty(value) ? 0L: Long.parseLong(value), true,true,sql,args);
    }
    /**
     * 根据菜单数据权限，找有权限的顶级机构列表
     *
     * @return
     */
    public Set<Party> getTopList() {
        if (!isByRight || mapRight == null) { //不控制权限，直接取吧
            return PartyCache.getInstance().getChildren(SwConsts.DEF_ROOT_ID_LONG);
        }

        Set<Party> list = new HashSet<>();
        if (mapRight.isEmpty()) return list;
        Set<String> setExists = new HashSet<>();
        for (DataRightGroupItem item : mapRight.values()) {
            if (!item.isInclude()) continue; //不包含的对象，这里是计算根节点，不管就是了
            //本单位之类的已处理
            Party party = PartyCache.getInstance().get(item.getValue());
            if (party == null) continue;
            boolean isParentExists = false;
            //添加节点，注意：如果是已经添加了的单位的下级，则不要添加
            String[] pids = party.getLevelCode().split(SwConsts.SPLIT_CHAR);
            for (String pid : pids) {
                if (setExists.contains(pid)) {
                    isParentExists = true;
                    break;
                }
            }
            if (!isParentExists) {
                list.add(party);
                setExists.add(String.valueOf(party.getEntityId()));
            }
        }
        return list;
    }

    /**
     * 获取下级节点
     *
     * @param parentId 上级机构id
     * @return
     */
    public Set<Party> getChildren(long parentId) {
        Set<Party> children = PartyCache.getInstance().getChildren(parentId);
        if (!isByRight || mapRight == null || CommUtil.isEmpty(children)) { //不控制权限，直接取吧
            return children;
        }

        if (mapRight.isEmpty()) return null;

        children.removeIf(party -> !hasRight(party));
        return children;
    }

    /**
     * 是否拥有此单位权限
     *
     * @param party
     * @return
     */
    private boolean hasRight(Party party) {
        int nret = hasRight(party.getEntityId());
        if (nret != RIGHT_RET_NONE) return nret != RIGHT_RET_EX;
        String[] pids = party.getLevelCode().split(SwConsts.SPLIT_CHAR);
        for (String pid : pids) {
            nret = hasRight(Long.parseLong(pid));
            if (nret == RIGHT_RET_IN_CHILD) return true;
            if (nret == RIGHT_RET_IN_NOCHILD || nret == RIGHT_RET_EX) return false;
        }
        return false;
    }

    private int hasRight(long id) {
        if (mapRight == null || !mapRight.containsKey(String.valueOf(id))) return RIGHT_RET_NONE;
        DataRightGroupItem item = mapRight.get(String.valueOf(id));
        if (item.isInclude()) {
            if (item.getType()== BpmConst.DataRight.V_INCLUDE_CHILDREN) return RIGHT_RET_IN_CHILD;
            return RIGHT_RET_IN_NOCHILD;
        }
        if (!item.isInclude()) return RIGHT_RET_EX;
        return 0;
    }


    /**
     * 判断childId是否是parentId的子孙
     *
     * @param childId
     * @param parentId
     * @return
     */
    public boolean isChild(long childId, long parentId) {
        if (parentId<=0L) return true;
        Party party = PartyCache.getInstance().get(childId);
        if (party == null) return false;
        String parentIdStr = SwConsts.SPLIT_CHAR+parentId+SwConsts.SPLIT_CHAR;
        return (party.getLevelCode() + SwConsts.SPLIT_CHAR).contains(parentIdStr);
    }
    /**
     * 获取查询语句，使用子查询的方式，数据权限越复杂，构建的语句就越复杂
     *
     * @param sqlField    查询的表的单位字段
     * @param partyId     查询条件机构的值，可以为空
     * @param qnext       是否带下级 注意：此方法仅单位管理使用。
     * @param includeSelf 是否包含自己
     * @param sql         sql
     * @param args        参数
     */
    public void buildSqlWhere(String sqlField, long partyId, boolean qnext,
                                 boolean includeSelf, StringBuilder sql, SwMap args) {
        if (partyId>0L) {//前端页面传入了单位id
            Party party = PartyCache.getInstance().get(partyId);
            if (isByRight&&party==null) {
                sql.append(" 1 <> 1 ");
                return ;
            }
            if (party != null) {//单位不存在，则默认查所有，押后
                int nret = hasRight(partyId);
                if (nret == RIGHT_RET_IN_NOCHILD) {//不包含下级，则直接等于即可
                    sql.append(" ").append(sqlField).append(" =:_dr_partyId ");
                    args.put("_dr_partyId",partyId);
                    return;
                }
                if (nret == RIGHT_RET_EX) {//不包含，直接用顶级单位的id来筛选，应该是无数据返回
                    sql.append(" 1 <> 1 ");
                    return;
                }
            } else {
                partyId = SwConsts.DEF_ROOT_ID_LONG;
            }
        }

        StringBuilder sqlAnd = new StringBuilder(128);
        StringBuilder sqlOr = new StringBuilder(128);

        addPartySql(partyId, true, true, qnext, includeSelf, sqlAnd, sqlOr, args);

        if (mapRight != null) {
            for (DataRightGroupItem item : mapRight.values()) {
                long itemPartyId = Long.parseLong(item.getValue());
                if (item.getValue().equalsIgnoreCase(String.valueOf(partyId))) continue;
                if (!isChild(itemPartyId, partyId)) continue; //非指定partyId的下级，不用搭理
                addPartySql(itemPartyId, item.isInclude(), item.getType() == BpmConst.DataRight.V_INCLUDE_CHILDREN, true, true, sqlAnd, sqlOr, args);
            }
        }

        if (sqlAnd.length() == 0 && sqlOr.length() == 0) {
            return ;
        }



        if (sqlOr.length() > 0) {
            sql.append("\nEXISTS(\n" +
                    "  SELECT pt_id FROM " + EntityHelper.getSchemaTableName(Party.ENTITY_NAME) + " r2 WHERE r2.pt_id=" + sqlField);
            sql.append(" and (").append(sqlOr.substring(4)).append("))");
            return ;
        }
        if (sqlAnd.length() > 0) {
            sql.append("\n not EXISTS(\n" +
                    "  SELECT pt_id FROM " + EntityHelper.getSchemaTableName(Party.ENTITY_NAME)+ " r2 WHERE r2.pt_id=" + sqlField);
            sql.append(" and (").append(sqlAnd.substring(4)).append("))");
        }
    }
    private void addPartySql(long partyId, boolean isInclude, boolean hasChild, boolean qnext, boolean includeSelf, StringBuilder sqlAnd, StringBuilder sqlOr, SwMap args) {
        if (partyId<=0L) return;
        if (!hasChild && isInclude) {//不包含下级
            String p = String.format("_dr_pt_id_%s", new Random().nextInt(10));
            sqlOr.append(String.format(" or r2.pt_id=:%s",p));
            args.put(p,partyId);
            return;
        }
        Party party = PartyCache.getInstance().get(partyId);
        if (party == null) return;
        if (isInclude) {
            localBuildSqlPara(qnext, includeSelf, sqlOr, args, party);
        } else {
            localBuildSqlPara(qnext, includeSelf, sqlAnd, args, party);
        }
    }

    //构建子查询语句
    private void localBuildSqlPara(boolean qnext, boolean includeSelf, StringBuilder sqlOr,  SwMap args, Party party) {
        sqlOr.append(" or ( ");
        if (includeSelf) {
            String p = String.format("_dr_pt_id_%s", new Random().nextInt(10));
            sqlOr.append(String.format("(r2.pt_id=:%s or ",p));
            args.put(p,party.getId());
        }
        if (qnext) {
            String p = String.format("_dr_pt_level_code_%s", new Random().nextInt(10));
            sqlOr.append(String.format(" r2.pt_level_code like :%s",p));
            args.put(p,party.getLevelCode() + SwConsts.SPLIT_CHAR + party.getId() + "%");
        } else {
            String p = String.format("_dr_pt_parent_id_%s", new Random().nextInt(10));
            sqlOr.append(String.format(" r2.pt_parent_id = :%s",p));
            args.put(p,party.getId());
        }

        if (includeSelf) sqlOr.append("))");
        else sqlOr.append(")");
    }

}
