<?php
/**
 * Description of InfoFlow.php
 *
 * @author caoyunmeng
 * Date: 2021/3/1
 * Time: 2:32 PM
 */

namespace  Api\PhpServices\InfoFlow;

use Api\PhpUtils\Http\Request;
use Api\PhpServices\ThumbsUp\ThumbsUp;

class InfoFlow
{
    /**
     * 错误码
     */
    const ERROR_CODE = [
        1 => '参数有误',
        2 => '获取信息流失败',
        3 => '获取推荐信息流为空',
        4 => '获取推荐信息流错误',
        5 => '获取运营卡片错误',
        6 => '获取封面图失败'
    ];

    /**
     * 成功响应
     * @param null $data
     * @return array|int[]
     */
    private function success($data = null)
    {
        if (is_null($data)) {
            return ['code' => 0];
        }
        return ['code' => 0, 'data'=>$data];
    }

    /**
     * 异常响应
     * @param $code
     * @return array
     */
    private function error($code)
    {
        return ["code" => $code, "msg" => self::ERROR_CODE[$code]];
    }

    /**
     * @param $request_data array 并发请求参数拼接
     * @param string $appid 端标识
     * @param bool $userid  是否需要返回是否已点赞
     *
     * @return array
     */
    public function getInfoFLow($request_data, $appid, $userid = false)
    {
        $flow = $this->getInfos($request_data);

        //并发请求数据成功
        if(isset($flow['code']) && $flow['code'] == 0){
            $advert = [];
            if(isset($flow['data']['advert']) && !empty($flow['data']['advert'])){
                $advert = $flow['data']['advert'];
            }

            $tool_documents = [];
            if(isset($flow['data']['tool']) && !empty($flow['data']['tool'])){
                $tool = $flow['data']['tool'];
                if(isset($tool['code']) && $tool['code'] == 0 && !empty($tool['documents'])){
                    $tool_documents = $tool['documents'];
                }else{
                    return $this->error(5);
                }
            }

            if(isset($flow['data']['recommend']) && !empty($flow['data']['recommend'])){
                $recommend = $flow['data']['recommend'];
                if(isset($recommend['code']) && $recommend['code'] == 0){
                    $recommend_result = $recommend['response'];
                    if(empty($recommend_result)){
                        return $this->error(3);
                    }
                }else{
                    return $this->error(4);
                }
            }else{
                return $this->error(3);
            }

            $mix_flow = $this->mixSort($recommend_result, $tool_documents, $advert);

            //获取是否已点赞
            if($userid){
                $thumbup = new ThumbsUp();
                foreach ($mix_flow as &$item){
                    $docid = '';
                    if((isset($item['docid']) && !empty($item['docid']))){
                        $docid = $item['docid'];
                    }
                    if(isset($item['id']) && isset($item['type']) && $item['type'] == 'doc'){
                        $docid = $item['id'];
                        $item['docid'] = $item['id'];
                    }
                    if(!empty($docid)){
                        $item['is_like'] = $thumbup->hasThumbsUp($appid, $userid, $docid);
                    }
                }
            }

            return ['code' => 0, 'data' => $mix_flow];
        }else{
            return $flow;
        }
    }

    /**
     * 并发请求从推荐、运营、广告处获取相关信息列表
     * @param $request_data
     * @return array
     */
    public function getInfos($request_data)
    {
        //稍后会移到yaconf中
        $info_flow_keys = [
            0 => 'recommend',
            1 => 'tool',
            2 => 'advert'
        ];
        $keys = array_flip($info_flow_keys);
        $urls = $methods = $params = $timeouts = $content_types = $headers = [];
        foreach ($request_data as $info_flow_key => $info_flow_conf) {
            //验证必传参数
            if (!in_array($info_flow_key, $info_flow_keys) || empty($info_flow_conf)) {
                return $this->error(1);
            }
            if (!isset($info_flow_conf['url']) || empty($info_flow_conf['url'])) {
                return $this->error(1);
            }
            if (!isset($info_flow_conf['method']) || empty($info_flow_conf['method'])) {
                return $this->error(1);
            }
            $info_flow_conf['method'] = strtoupper($info_flow_conf['method']);
            if (!in_array($info_flow_conf['method'], ['GET', 'POST'])) {
                return $this->error(1);
            }
            if (!isset($info_flow_conf['timeout']) || empty($info_flow_conf['timeout'])) {
                return $this->error(1);
            }
            //验证非必传参数
            if (isset($info_flow_conf['params']) && empty($info_flow_conf['params'])) {
                return $this->error(1);
            }
            if (isset($info_flow_conf['content_type']) && empty($info_flow_conf['content_type'])) {
                return $this->error(1);
            }
            if (isset($info_flow_conf['headers']) && empty($info_flow_conf['headers'])) {
                return $this->error(1);
            }
            //构造请求参数
            $_key = $keys[$info_flow_key];
            $urls[$_key] = $info_flow_conf['url'];
            $methods[$_key] = $info_flow_conf['method'];
            $timeouts[$_key] = $info_flow_conf['timeout'];
            if (isset($info_flow_conf['params'])) {
                $params[$_key] = $info_flow_conf['params'];
            }
            if (isset($info_flow_conf['content_type'])) {
                $content_types[$_key] = $info_flow_conf['content_type'];
            }
            if (isset($info_flow_conf['headers'])) {
                $headers[$_key] = $info_flow_conf['headers'];
            }
        }
        $request = new Request();
        $ret = $request->concurrencyBlend($urls, $methods, $params, $timeouts, $content_types, $headers);
        if (empty($ret)) {
            return $this->error(2);
        }
        $list = [];
        foreach ($ret as $k => $v) {
            $_key = $info_flow_keys[$k];
            $list[$_key] = $v;
        }
        return $this->success($list);
    }

    /**
     * 将相关信息流列表做融合
     * @param array $recommend 推荐信息流数据（二维数组）
     * @param array $tool 运营卡片数据（二维数组）
     * @param array $advert 广告数据（二维数组）
     * @return array
     */
    public function mixSort(array $recommend=[], array $tool=[], array $advert=[])
    {
        // 定义每个上游数据根据各方权重出现的位置
        // 各方权重:
        // 推荐流--recommend -1，3
        // 运营卡片--tool 1，
        // 广告--advert 1,2；各方出现顺序 blender -1,miss 1,miss 2,blender 3 根据权重出现的顺序 1,2,3,4 暂时不考虑广告
        $card_position = [
            'recommend'=>[
                -1 => 1,
                3 => 4,
            ],
            'tool'=>[
                1 => 2,
                2 => 3
            ],
            // advert的这期咱不考虑
            // 'advert'=>[
            //     1=>3,
            //     2=>5
            // ]
        ];

        // order_arr 展示顺序 weight_arr 权重
        $order_arr = $weight_arr = [];
        foreach ($card_position as $cval) {
            foreach ($cval as  $cckey=>$ccval) {
                array_push($order_arr, $ccval);
                array_push($weight_arr, $cckey);
            }
        }

        // 增加各方数据标识--card_source、各方根据权重出现的顺序--card_position_order 字段
        // $recommend 数据
        $recommend = array_map(function ($recommend_val) use ($card_position) {
            $recommend_val['card_source'] = 'recommend'.$recommend_val['card_number']; // 给card_source增加序号，为的是好看数据
            if (isset($card_position['recommend'][$recommend_val['card_weight']])) {
                $recommend_val['card_position_order'] = $card_position['recommend'][$recommend_val['card_weight']];
            } else {
                // 不在排序中的数据，位置出现顺序就给一个默认值
                $recommend_val['card_position_order'] = -99;
            }
            return $recommend_val;
        }, $recommend);

        // 运营数据
        $tool = array_map(function ($tool_val) use ($card_position) {
            $tool_val['card_source'] = 'tool'.$tool_val['card_number']; // 给card_source增加序号，为的是好看数据
            if (isset($card_position['tool'][$tool_val['card_weight']])) {
                $tool_val['card_position_order'] = $card_position['tool'][$tool_val['card_weight']];
            } else {
                $tool_val['card_position_order'] = -99;
            }
            return $tool_val;
        }, $tool);

        // todo 如果需要考虑广告时，可以增加广告的

        // 合并各方数据
        $all_array = array_merge($recommend, $tool);
        // 初始化新数组
        $new_arr = [];
        // 计算需要初始化数组的长度
        $total_new_arr_count = count($all_array) ;
        for ($i=0;$i<$total_new_arr_count;$i++) {
            $new_arr[$i] = [];
        }
        // need_sort_data 存放根据权重需要排序的数据; nosort_other_data 存放不需要排序的数据
        $need_sort_data = $nosort_other_data = $need_card_source = [];
        // 根据权重做数据分组
        $this->getRecDataByWeight($all_array, $need_sort_data, $nosort_other_data, $need_card_source, $weight_arr, $order_arr);
        // 根据出现顺序从小到大排序 顺序越小的，优先考虑根据位置放入新的数组中
        sort($order_arr);
        foreach ($order_arr as $wval) {
            if (!isset($need_sort_data[$wval]) || empty($need_sort_data[$wval])) {
                continue;
            }
            // 依次处理数据
            $this->sortWeightData($new_arr, $need_sort_data[$wval]);
        }
        $again_news_data = [];
        $all_card_source = $need_card_source;
        $news_card_source = array_column($new_arr, 'card_source');
        $again_news_arr = array_diff($all_card_source, $news_card_source);
        if (!empty($again_news_arr)) {
            foreach ($all_array as $all_key => $all_val) {
                if (in_array($all_val['card_source'], $again_news_arr)) {
                    $again_news_data[$all_key] = $all_val;
                }
            }
        }
        // 最后合并不规则数据
        $new_arr = array_values(array_filter(array_merge($new_arr, $again_news_data, $nosort_other_data)));
        return $new_arr;
    }

    /**
     * 根据权重和每个权重要出现的位置划分数据
     * @param array $arr 参与排序的数据
     * @param array $need_sort_data 需要根据权重排序的数据
     * @param array $nosort_other_data 不需要参与根据权重排序的数据
     * @param array $limit_arr 权重数组
     * @param array $order_arr 排序数组
     * @param mixed $need_card_source
     * @return array
     */
    private function getRecDataByWeight($arr, &$need_sort_data, &$nosort_other_data, &$need_card_source, $limit_arr = [], $order_arr = [])
    {
        foreach ($arr as $value) {
            // 根据每个权重要出现的位置顺序 + 序号 进行数据分组
            if (!empty($limit_arr) && in_array($value['card_weight'], $limit_arr) && in_array($value['card_position_order'], $order_arr)) {
                $need_sort_data[$value['card_position_order']][$value['card_number']] = $value;
                $need_card_source[] = $value['card_source'];
            } else {
                $nosort_other_data[] = $value;
            }
        }

        return ['need_sort_data'=>$need_sort_data, 'nosort_other_data'=>$nosort_other_data, 'need_card_source'=>$need_card_source];
    }

    /**
     * 根据权重排序
     * @param array $news_arr 每次排序好的数据
     * @param array $sort_data 需要排序的数据
     * @return mixed
     */
    private function sortWeightData(&$news_arr, $sort_data=[])
    {
        // position_find_data 需要重新找位置的数据
        $position_find_data = [];
        // 1.1 根据顺序放入对应的数据
        foreach ($news_arr as $news_key => &$news_value) {
            if (isset($sort_data[$news_key])) {
                if (empty($sort_data[$news_key])) {
                    continue;
                }
                // 根据权重顺序+序号card_number放入新数组中对应的序号
                if (empty($news_value)) {
                    $news_arr[$news_key] = $sort_data[$news_key];
                } else {
                    // 同一个序号被高权重的数据占了，把挤出来的数据放入到一个数组中，重新找距离它最近的位置
                    array_push($position_find_data, $sort_data[$news_key]);
                }
            }
        }
        // 1.2 处理被挤出来的数据的位置
        if (!empty($position_find_data)) {
            foreach ($news_arr as $new_key => &$new_value) {
                // 被挤出来的数据依次找到位置之后，直接结束本次循环
                if (empty($position_find_data)) {
                    break;
                }
                // 获取第一组数据
                $first_data = current($position_find_data);
                //  位置已经被权重高的给占位了 或者找位置的card_number > 可用的空位置时 则跳过 保证依次向下找最近的位置
                if (!empty($new_value) || ($new_key < $first_data['card_number'])) {
                    continue;
                }
                // 依次放入数据
                $news_arr[$new_key] = array_shift($position_find_data);
            }
        }
        return $news_arr;
    }

    public function selectCovers($doc_ids, $userid = 0){
        $url = config('interface', 'service.flow.cover_select');
        $params = [
            'docids' => implode(',', $doc_ids),
            'userid' => $userid
        ];
        $timeout = 3000;
        //获取文章详情
        $ret = (new Request())->post($url, $params, $timeout);
        if (!empty($ret['code']) || !isset($ret['response']['result']) || empty($ret['response']['result'])) {
            return $this->error(6);
        }
        return $this->success($ret['response']['result']);
    }
}
