package com.quantgroup.asset.distribution.service.rule.vo;

import com.alibaba.fastjson.annotation.JSONType;
import com.google.common.base.Joiner;
import com.google.common.base.Objects;
import com.google.common.collect.Lists;
import com.quantgroup.asset.distribution.enums.UnionType;
import com.quantgroup.asset.distribution.exception.QGException;
import com.quantgroup.asset.distribution.exception.QGExceptionType;
import com.quantgroup.asset.distribution.exception.QGPreconditions;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.collections.MapUtils;
import org.springframework.util.CollectionUtils;

import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * {condition:[
 * {key:’v5’, operator:’<‘, value:’0.2’},
 * {key:’v4’, operator:’in‘, value:’100,200,4900’},
 * {condition:[{key:’v5’, operator:’<‘, value:’0.2’},{key:’v4’, operator:’<‘, value:’100’}], operator:’or’}
 * ], operator:’and’}
 * <p>
 * v5<0.2 && v4<100 && (v5<0.2 || v4<100)
 * <p>
 * {condition:[{key:’v5’,operator:’<‘,value:’0.2’},{key:’v4’,operator:’<‘,value:’100’}],operator:’and’}
 */
@Data
@NoArgsConstructor
@JSONType(typeName = "union")
public class UnionRuleVO implements IRuleVO, Serializable {
    private static final long serialVersionUID = -1L;
    private UnionType unionType;
    private List<UnionRuleVO> unionRules = Lists.newArrayList();
    private List<BaseRuleVO> baseRules = Lists.newArrayList();

    public UnionRuleVO(UnionType unionType) {
        this.unionType = unionType;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        UnionRuleVO that = (UnionRuleVO) o;
        return unionType == that.unionType &&
                Objects.equal(unionRules, that.unionRules) &&
                Objects.equal(baseRules, that.baseRules);
    }

    @Override
    public boolean valid(Map<String, Object> data) {
        QGPreconditions.checkArgument(!(CollectionUtils.isEmpty(baseRules) && CollectionUtils.isEmpty(unionRules)), QGExceptionType.RULE_IS_NOT_JSON);
        switch (unionType) {
            case Or:
                return unionRules.stream().anyMatch(r -> r.valid(data))
                        || baseRules.stream().anyMatch(r -> r.valid(data));
            case And:
                return  unionRules.stream().allMatch(r -> r.valid(data))
                        &&  baseRules.stream().allMatch(r -> r.valid(data));
            default:
                throw new QGException(QGExceptionType.RULE_UNION_OPERATOR_NOT_EXIST);
        }
    }

    @Override
    public Map<String, Boolean> valid2(Map<String, Object> data) {
        Map<String,Boolean> maps = new HashMap<>();
        Map<String, Boolean> umap = unionRules.parallelStream().collect(Collectors.toMap(k -> k.format(), v -> v.valid(data)));
        Map<String, Boolean> bmap = baseRules.parallelStream().collect(Collectors.toMap(k -> k.format(), v -> v.valid(data)));
        if(MapUtils.isNotEmpty(umap))
            maps.putAll(umap);
        if(MapUtils.isNotEmpty(bmap))
            maps.putAll(bmap);
        return maps;
    }



    @Override
    public String format() {
        StringBuilder stringBuilder = new StringBuilder();
        String operator = unionType == UnionType.And ? "&&" : "||";
        stringBuilder.append(Joiner.on(operator).join(baseRules.parallelStream().map(BaseRuleVO::format).collect(Collectors.toList())));
        if (!CollectionUtils.isEmpty(unionRules)) {
            if (!CollectionUtils.isEmpty(baseRules))
                stringBuilder.append(operator);
            stringBuilder.append(Joiner.on(operator).join(unionRules.parallelStream().map(r -> "(" + r.format() + ")").collect(Collectors.toList())));
        }
        return stringBuilder.toString();
    }


    @Override
    public String getId() {
        return null;
    }

    @Override
    public void checkParams() {

    }

    @Override
    public Set<String> getParamNames() {
        Set<String> keys = baseRules.parallelStream().map(BaseRuleVO::getKey).collect(Collectors.toSet());
        if (!CollectionUtils.isEmpty(unionRules))
            unionRules.parallelStream().forEach(unionRuleVO -> keys.addAll(unionRuleVO.getParamNames()));
        return keys;
    }
}




