<?php 

namespace App\Services; 

use App\Feature;

Class FeatureService
{
    public function getMapArray()
    {
        $features = Feature::query()->get();
        $map = [];
        foreach ($features as $feature){
            $map[$feature->id] = ["type"=>$feature->type,"logic"=>$feature->logic,"describe"=>$feature->describe];
        }
        return $map;
    }

    public function translationFeature($str)
    {
        if (!$str)return null;
        $result = [];
        preg_match_all('/\d+|[\&\|\(\)]/',$str,$result); //初次匹配以数字&|()为分隔符生成数组
        $sign = 0;  //为第二次切割做起点标记
        $model = [];//第二次切割数组
        $ids = [];//记录出现的特征ID，统一查询
        foreach ($result[0] as $index => &$str){
            if (is_numeric($str)){
                $model[] = array_slice($result[0],$sign,($index-$sign)+1);
                $sign = $index+1;
                $ids[] = $str;
                continue;
            }
            if ($index == count($result[0])-1 && $str == ')'){
                $model[] = [")"];
            }
        }//以数字为标准切割策略组
        $features = Feature::query()->find($ids);//查询出现的特征
        $featureMap = [];
        foreach ($features as $index => $feature){
            $featureMap[$feature->id] = $index;
        }//为查询的特征重组为key-val形式数组留做引用
        foreach ($model as $index => &$m){
            $arr = [
                "strategyGroupStartSign" => false,//是否为策略组起点,这将在解析时解析为 (
                "calculation" => "",//运算规则，目前仅有 &,| 翻译后填入
                "type"=>"",  //特征类型
                "id"=>"",  //特征ID
                "logic"=>"",  //特征逻辑
                "describe"=>"",  //特征信息
                "strategyGroupEndSign" => false,//是否为策略组终点，这将在解析时解析为 )
            ];//最终对象组模型,策略组将几组特征组合引用
            foreach ($m as $str){
                if (is_numeric($str)){//填入特征信息
                    if (isset($featureMap[$str])){
                        $arr["type"] = $features[$featureMap[$str]]->type;
                        $arr["id"] = $features[$featureMap[$str]]->id;
                        $arr["logic"] = $features[$featureMap[$str]]->logic;
                        $arr["describe"] = $features[$featureMap[$str]]->describe;
                    }
                    continue;
                }
                switch ($str){//特殊字符的翻译
                    case "(":
                        $arr["strategyGroupStartSign"] = true;
                        break;
                    case ")":
                        $model[$index-1]["strategyGroupEndSign"] = true;
                        break;
                    case "&":
                        $arr["calculation"] = "并且";
                        break;
                    case "|":
                        $arr["calculation"] = "或";
                        break;
                }
            }
            if (!$arr["id"]){
                unset($model[$index]);
                continue;
            }
            $m = $arr;//变更当前指针为翻译结果
        }
        return $model;
    }

    public function analysisFeature($features)
    {
        $str = "";
        $map = [];
        foreach ($features as &$feature){
            if (!$feature["type"] || !$feature["logic"] || !$feature["describe"])continue;
            $f = Feature::query()->firstOrCreate([
                "type"=>$feature["type"],  //特征类型
                "logic"=>$feature["logic"],  //特征逻辑
                "describe"=>$feature["describe"],  //特征信息
            ]);
            $feature["id"] = $f->id;
            $map[$feature["id"]] = $f;
            if ($feature["calculation"] == '并且')$str .= '&';
            else $str .= '|';
            if ($feature["strategyGroupStartSign"])$str .= "(";
            $str .= $feature["id"];
            if ($feature["strategyGroupEndSign"])$str .= ")";
        }
        return ['feature'=>$str,"map"=>$map];
    }

    public function formatFeature(array $features, $value, $columnMapping = null)
    {
        if (!$features)return $value;
        preg_match_all('/\d+|[\&\|\(\)]/',$value,$result);
        foreach ($result[0] as &$str){
            if (is_numeric($str) && isset($features[$str])){
                $column = $features[$str]["type"];
                $logic = $features[$str]["logic"];
                $describe = $features[$str]["describe"];
                if ($columnMapping){
                    $column = $columnMapping[$column] ?? $column;
                    switch ($logic){
                        case "包含":
                            $logic = " like ";
                            $describe = "%".$describe."%";
                            break;
                        case "不包含":
                            $logic = " not like ";
                            $describe = "%".$describe."%";
                            break;
                        case "等于":
                            $logic = " = ";
                            break;
                    }
                    $str = $column.$logic.$describe;
                }else $str = $features[$str]["type"].' '.$features[$str]["logic"].' '.$features[$str]["describe"];
            }
            if ($str == "&"){
                if ($columnMapping) $str = " and ";
                else $str = " 并且 ";
            }
            if ($str == "|"){
                if ($columnMapping) $str = " or ";
                else $str = " 或 ";
            }
        }
        return implode("",$result[0]);
    }

    public function matchFeature($value, $columnMapping, $matchObject) :bool
    {
        preg_match_all('/\d+/',$value,$ids);
        if ($ids[0])$fs = Feature::query()->whereIn("id",$ids[0])->get();
        else return false;
        $features = [];
        foreach ($fs as $f){
            $features[$f->id] = $f;
        }
        preg_match_all('/\d+|[\&\|\(\)]/',$value,$result);
        foreach ($result[0] as &$str) {
            if (is_numeric($str) && isset($features[$str])) {
                $column = $features[$str]["type"];
                $logic = $features[$str]["logic"];
                $describe = $features[$str]["describe"];
                $value = isset($columnMapping[$column]) ? $matchObject[$columnMapping[$column]] : '';
                switch ($logic) {
                    case "包含":
                        $str = mb_strpos($value,$describe) === false ? 'false' : 'true';
                        break;
                    case "不包含":
                        $str = mb_strpos($value,$describe) === false ? 'true' : 'false';
                        break;
                    case "等于":
                        $str = $value == $describe ? 'true' : 'false';
                        break;
                }
                continue;
            }
            if ($str == "&") {
                $str = '&&';
                continue;
            }
            if ($str == "|") {
                $str = '||';
            }
        }
        $is = implode("",$result[0]);
        return eval("return $is;");
    }

}