<?php

namespace App\Services\Pair;

use App\Services\Pair\DataModels\PairMatchDataSource;
use Illuminate\Support\Facades\Redis;

abstract class BasePairMatch
{
    /** @var int 匹配期数 */
    protected $stageId;

    /** @var array 匹配结果 */
    public $matchResult = array();

    /** @var PairMatchDataSource[] 数据源 */
    protected $dataSources = array();

    /** @var PairMatchDataSource[] 垃圾桶数据源  */
    private $trashDataSources = array();

    /** @var PairMatchDataSource 基准对比源 */
    private $baseDataSource;

    /** @var string 定位缓存key */
    private $LBSCacheKey;

    /** @var array 匹配规则数组 */
    private $matchRuleFuncs = array('radius', 'age', 'height', 'gender', 'noOnce');

    public function __construct()
    {
    }

    /**
     * 加载匹配数据源
     */
    abstract public function loadMatchData();

    /**
     * 添加匹配规则
     * @param array|string $rule
     */
    protected function addMatchRule($rule)
    {
        if (is_array($rule)) {
            array_map(function ($item) {
                array_push($this->matchRuleFuncs, $item);
            }, $rule);
        } else {
            array_push($this->matchRuleFuncs, $rule);
        }
        $this->matchRuleFuncs = array_unique($this->matchRuleFuncs);
    }

    /**
     * 移除匹配规则
     * @param array|string $rule
     */
    protected function removeMatchRule($rule)
    {
        if (!is_array($rule)) {
            $rule = array($rule);
        }
        $this->matchRuleFuncs = array_diff($this->matchRuleFuncs, $rule);
    }

    /**
     * 是否匹配范围
     * @param  PairMatchDataSource  $dataSource
     * @return bool
     */
    public function isMatchRadius(PairMatchDataSource $dataSource): bool
    {
        if (! $this->baseDataSource->rangeUsers) {
            $this->baseDataSource->rangeUsers = Redis::geoRadius($this->LBSCacheKey, $this->baseDataSource->enrollLongitude, $this->baseDataSource->enrollLatitude, $this->baseDataSource->matchRadius, 'km');
        }
        if (! $dataSource->rangeUsers) {
            $dataSource->rangeUsers = Redis::geoRadius($this->LBSCacheKey, $dataSource->enrollLongitude, $dataSource->enrollLatitude, $dataSource->matchRadius, 'km');
        }
        if (in_array($dataSource->uid, $this->baseDataSource->rangeUsers) && in_array($this->baseDataSource->uid, $dataSource->rangeUsers)) {
            return true;
        }
        return false;
    }

    /**
     * 是否匹配年龄
     * @param  PairMatchDataSource  $dataSource
     * @return bool
     */
    public function isMatchAge(PairMatchDataSource $dataSource): bool
    {
        return false;
    }

    /**
     * 是否匹配身高
     * @param  PairMatchDataSource  $dataSource
     * @return bool
     */
    public function isMatchHeight(PairMatchDataSource $dataSource): bool
    {
        return false;
    }

    /**
     * 是否匹配性别
     * @param  PairMatchDataSource  $dataSource
     * @return bool
     */
    public function isMatchGender(PairMatchDataSource $dataSource): bool
    {
        if (
            $this->baseDataSource->gender == $dataSource->sexualOrientation &&
            $this->baseDataSource->sexualOrientation = $dataSource->gender
        ) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 是否匹配过
     * @param  PairMatchDataSource  $dataSource
     * @return bool
     */
    public function isMatchNoOnce(PairMatchDataSource $dataSource): bool
    {
        return false;
    }

    /**
     * 任务初始化
     */
    public function taskInit()
    {
        $this->LBSCacheKey = "pair:{$this->stageId}:lsb";
    }

    public function matchRules(PairMatchDataSource $dataSource): bool
    {
        $boolMatch = true;
        foreach ($this->matchRuleFuncs as $func) {
            if (method_exists($this, "isMatch" . ucfirst($func))) {
                $boolMatch = $boolMatch && $this->{"isMatch" . ucfirst($func)}($dataSource);
            } else {
                $boolMatch = $boolMatch && false;
            }
        }
        return $boolMatch;
    }

    private function match()
    {
        $this->trashDataSources = array();

        // 结束指针
        array_push($this->dataSources, null);

        $this->baseDataSource = array_shift($this->dataSources);
        while ($this->baseDataSource) {
            $dataSource = array_shift($this->dataSources);
            while ($dataSource) {
                if ($this->matchRules($dataSource)) {
                    array_push($this->matchResult, [
                        $this->baseDataSource,
                        $dataSource
                    ]);
                    $this->baseDataSource = null;
                    break;
                } else {
                    array_push($this->dataSources, $dataSource);
                    $dataSource = array_shift($this->dataSources);
                }
            }
            $this->baseDataSource && array_push($this->trashDataSources, $this->baseDataSource);
            $this->baseDataSource = array_shift($this->dataSources);
        }
    }

    /**
     * 执行任务
     * @return void
     */
    public function run()
    {
        $this->loadMatchData();
        $this->taskInit();
        $this->match();
        $this->dataSources = $this->trashDataSources;
        $this->removeMatchRule('radius');
        $this->match();
    }
}
