<?php

namespace App\Models\Fpdx;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use App\Exceptions\AlertException;
use App\Models\User\UserModel;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Redis;

class PairModel extends Model
{
    protected $table = 'kdgx_partner_charge_pair';

    protected $dateFormat = 'U';

    public const CREATED_AT = 'create_time';

    protected $fillable = [
        'uid',
        'stage_id',
        'activity_type',
        'assoc_id',
        'partner',
        'pair_type',
        'state',
        'order_id',
        'media_id',
        'sex',
        'sxo',
        'min_age',
        'max_age',
        'height',
        'birthday',
        'device',
        'km',
        'lng',
        'lat',
        'location',
        'data',
        'score',
        'pay',
        'add_score',
        'allow_photo',
        'confirm',
        'repair',
        'last_assoc',
        'rematch',
        'keep',
        'hidden_at'
    ];


    /**
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function activity()
    {
        return $this->belongsTo(ActivityModel::class, 'stage_id', 'stage_id');
    }

    /**
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function user()
    {
        return $this->belongsTo(\App\Models\User\UserModel::class, 'uid', 'uid')
            ->select(
                'nickname',
                'headimgurl',
                'sex',
                'sxo',
                'height',
                'age',
                'home',
                'address',
                'school',
                'qq',
                'weixin',
                'pair_min_age',
                'pair_max_age',
                'wxkf'
            );
    }

    /**
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function media()
    {
        return $this->belongsTo(MediaModel::class, 'media_id', 'media_id');
    }

    /**
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function room()
    {
        return $this->belongsTo(RoomModel::class, 'room_id', 'room_id');
    }


    /**
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function group()
    {
        return $this->belongsTo(GroupModel::class, 'group_id', 'id');
    }

    /**
     * 没有隐藏的列表
     * @param $query
     * @return mixed
     */
    public function scopeHidden(/** @var Builder $query */$query)
    {
        return $query->where('hidden_at', 0);
    }

    /**
     * 获取匹配范围内的用户
     * @return array
     */
    public function getMatchRadiusUids(): array
    {
        $lbsCacheKey = "pair:{$this->stageId}:lsb";
        $uids = Redis::geoRadius($lbsCacheKey, $this->lng, $this->lat, $this->match_raduis, 'km');
        return $uids;
    }

    /**
     * 获取报名信息
     * @param $stage_id
     * @param $uid
     * @return mixed
     * @throws AlertException
     */
    public function getPairByStageUid($stage_id, $uid)
    {
        $where = [
            ['stage_id', '=', $stage_id],
            ['uid', '=', $uid]
        ];
        $pair = $this->where($where)->first();
        if (collect($pair)->isEmpty()) {
            throw new AlertException("未能找到报名记录");
        } else {
            return $pair;
        }
    }

    /**
     * 获取「某期」待匹配的队列
     * @param  int  $stage_id  期数
     * @param  string  $activity_type
     * @param  string|null  $city
     * @return Collection
     */
    public function getPairQuery(int $stage_id, string $activity_type = "72h", string $city = null)
    {
        $pairs = $this->where([['stage_id', $stage_id], ['activity_type', $activity_type]])
            ->whereNull('assoc_id')
            ->whereBetween('state', [100, 299])
            ->get(['uid']);
        /** @var Collection $users */
        $users = UserModel::whereIn('uid', $pairs->pluck('uid'))
            ->when(!is_null($city), function (/** @var Builder $query */ $query) use ($city) {
                return $query->where('address', $city);
            })->get([
                'uid',
                'sex',
                'sxo',
                'height',
                'age',
                'home',
                'address',
                'school',
                'fpdx_like',
                'pair_min_age',
                'pair_max_age',
                'pair_province',
                'pair_all'
            ]);
        foreach ($users as &$user) {
            $pair = $this->where([['stage_id', $stage_id], ['uid', $user->uid]])->first(['id', 'score']);
            if (!collect($pair)->isEmpty()) {
                $user->pair_id = $pair->id;
                $user->score = $pair->score;
                $pairs = Redis::smembers("fpdx_pairs_{$user->uid}");
                if (empty($pairs)) {
                    $pairs = array();
                }
                $user->pairs = $pairs;
            }
        }
        $users = $users->sortByDesc("score");
        return $users;
    }

    /**
     * 重新匹配列表
     * @param  int  $stage_id
     * @param  string  $activity_type
     * @param  string|null  $city
     * @return Collection
     */
    public function getRematchPair(int $stage_id, string $activity_type = "72h", string $city = null)
    {
        $pairs = $this->where([
            ['stage_id', $stage_id],
            ['rematch', 1],
            ['activity_type', $activity_type]
        ])->whereNull('assoc_id')->get(['uid']);
        /** @var Collection $users */
        $users = UserModel::whereIn('uid', $pairs->pluck('uid'))
            ->when(!is_null($city), function (/** @var Builder $query */ $query) use ($city) {
                return $query->where('address', $city);
            })->get([
                'uid',
                'sex',
                'sxo',
                'height',
                'age',
                'home',
                'address',
                'school',
                'fpdx_like',
                'pair_min_age',
                'pair_max_age',
                'pair_province',
                'pair_all'
            ]);
        foreach ($users as &$user) {
            $pair = $this->where([['stage_id', $stage_id], ['uid', $user->uid]])->first(['id', 'score']);
            if (!collect($pair)->isEmpty()) {
                $user->pair_id = $pair->id;
                $user->score = $pair->score;
                $pairs = Redis::smembers("fpdx_pairs_{$user->uid}");
                if (empty($pairs)) {
                    $pairs = array();
                }
                $user->pairs = $pairs;
            }
        }
        $users = $users->sortByDesc("score");
        return $users;
    }

    /**
     * 获取某期匹配对象的回复情况
     * @param  int stage_id 期数
     * @return object data
     * [
     *      {
     *           id: 匹配id
     *           uid: 用户id
     *           stage_id: 报名期数
     *           media_id: 报名公众号id
     *           assoc_id: 匹配对象匹配id
     *           receive: 是否回复过 [true | false]
     *           pair: {
     *               id: 匹配id
     *               uid: 用户id
     *               stage_id: 报名期数
     *               media_id: 报名公众号id
     *               assoc_id: 匹配对象匹配id
     *               receive: 是否回复过 [true | false]
     *           }
     *      }
     * ]
     */
    public function getAllReceiveState(int $stage_id)
    {
        $where = array(
            ['stage_id', $stage_id],
            ['sex', 2],
            ['assoc_id', '!=', null]
        );
        $girls = self::where($where)->get();
        $where = array(
            ['stage_id', $stage_id],
            ['sex', 1],
            ['assoc_id', '!=', null]
        );
        $boys = self::where($where)->get();
        $boys = $boys->keyBy('id');
        foreach ($girls as &$girl) {
            if (isset($girl->receives) && $girl->receives > 0) {
                $girl->receive = true;
            } else {
                $girl->receive = false;
            }
            $pair = $boys->get($girl->assoc_id);
            if (isset($pair->receives) && $pair->receives > 0) {
                $pair->receive = true;
            } else {
                $pair->receive = false;
            }
            $girl->pair = $pair;
        }
        return $girls;
    }

    /**
     * 获取某期某人及其对象回复情况
     * @param  int  $stage_id
     * @param  int  $uid
     * @return object data
     * {
     *      id: 匹配id
     *      uid: 用户id
     *      stage_id: 报名期数
     *      media_id: 报名公众号id
     *      assoc_id: 匹配对象匹配id
     *      receive: 是否回复过 [true | false]
     *      pair: {
     *          id: 匹配id
     *          uid: 用户id
     *          stage_id: 报名期数
     *          media_id: 报名公众号id
     *          assoc_id: 匹配对象匹配id
     *          receive: 是否回复过 [true | false]
     *      }
     * }
     * @throws AlertException
     */
    public function getReceiveState(int $stage_id, int $uid)
    {
        $where = array(
            ['stage_id', $stage_id],
            ['uid', $uid],
            ['assoc_id', '!=', null]
        );
        $data = self::where($where)->first();
        if (collect($data)->isEmpty()) {
            throw new AlertException("未找到报名纪录");
        }
        if (isset($data->receives) && $data->receives > 0) {
            $data->receive = true;
        } else {
            $data->receive = false;
        }
        $pair = self::find($data->assoc_id);
        if (isset($pair->receives) && $pair->receives > 0) {
            $pair->receive = true;
        } else {
            $pair->receive = false;
        }
        $data->pair = $pair;
        return $data;
    }

    /**
     * 维护发送信息条数
     * @param  int uid 发送消息的用户uid
     * @param  int room_id 房间号
     * @param  int message_id 消息id
     * @return void
     */
    public static function receiveMsg(int $uid, int $room_id, int $message_id)
    {
        $room = RoomModel::find($room_id);
        if (collect($room)->isEmpty()) {
            return;
        } else {
            $msg = MessageModel::find($message_id);
            if (isset($msg->msg_type) && $msg->msg_type == -1) {
                self::where('stage_id', $room->stage_id)->where('uid', $uid)->increment('receives', 1);
            }
            return;
        }
    }

    /**
     * 获取某期不满意的报名集合
     * @param  int stage_id 期数
     * @return array result [1,2,3,8,...]
     */
    public static function getNotSatisfactions(int $stage_id)
    {
        $result = array();
        $where = array(
            ['stage_id', $stage_id],
            ['sex', 2],
            ['assoc_id', '!=', null]
        );
        $girls = self::where($where)->get();
        $where = array(
            ['stage_id', $stage_id],
            ['sex', 1],
            ['assoc_id', '!=', null]
        );
        $boys = self::where($where)->get();
        $boys = $boys->keyBy('id');
        foreach ($girls as &$girl) {
            try {
                $pair = $boys->get($girl->assoc_id);
                if ($girl->receives > 0 && 0 == $pair->receives) {
                    array_push($result, $girl->id);
                }
                if ($pair->receives > 0 && 0 == $girl->receives) {
                    array_push($result, $pair->id);
                }
            } catch (\Exception $e) {
                continue;
            }
        }
        return $result;
    }

    /**
     * 获取某期匹配成功的对象的信息
     * @param  int stage_id 期数
     * @return Collection
     * {
     *  [
     *      "id": 报名纪录id,
     *      "uid": 用户id,
     *      "stage_id": 期数,
     *      "room": {
     *          "room_id": 房间id,
     *          "room_name": 房间名称,
     *          "stage_id": 期数,
     *          "uid": 用户id,
     *          "pair_uid": 匹配对象uid,
     *          "create_time": unix时间戳
     *      }
     *  ]
     * }
     */
    public function getStageNumber($stage_id)
    {
        $pairs = $this->where('stage_id', $stage_id)->whereBetween('state', [400, 499])->get(['id', 'uid', 'stage_id']);
        $roomModel = new RoomModel();
        foreach ($pairs as $key => &$value) {
            try {
                $value->room = $roomModel->getRoom($value->room_id, $value->uid);
            } catch (\ApiException $e) {
                $pairs->forget($key);
                continue;
            }
        }
        return json_decode($pairs->toJson());
    }

    /**
     * 获取分配对象收益明细
     * @param  string media_id 公众号id
     * @param  int  $stage_id  期数
     * @return object 明细信息
     */
    public function fxIncome(string $media_id, int $stage_id = 0)
    {
        $data = $this->with([
            'user' => function (/** @var Builder $query */ $query) {
                $query->select(['uid', 'nickname']);
            }
        ])->where(array(
            ['media_id', $media_id],
            ['stage_id', '>', 26]
        ))->whereBetween('state', [100, 899])->when(
            $stage_id,
            function (/** @var Builder $query */ $query) use ($stage_id) {
                return $query->where('stage_id', $stage_id);
            }
        )->orderBy('create_time', 'desc')->get([
            'uid',
            'stage_id',
            'sex',
            'create_time',
            'state',
            'fx_money',
            'completed_at'
            ]);
        return $data;
    }

    /**
     * 分销排行
     * @param  int stage_id 期数
     * @return array [
     *      {
     *          'count': 数量,
     *          'fx_money': 收益,
     *          'media_id': 公众号id,
     *          'public_name': '公众号名称‘,
     *          'expect_money': '期望收益'
     *      }
     * ]
     */
    public function fxrank(int $stage_id = 0)
    {
        $hides = Redis::lrange("{pair_hides}", 0, -1);
        $hidesql = "";
        foreach ($hides as $value) {
            $hidesql .= "'{$value}',";
        }
        if ($stage_id != 0) {
            $sql = "select count(*) as count, p.media_id, public_name from kdgx_partner_charge_pair as p join kdgx_partner_charge_media as m on p.media_id=m.media_id where `stage_id`= ? and `state` between 100 and 899 and m.`type` = 0 and p.`media_id` not in ({$hidesql}'+') GROUP BY p.`media_id`";
            $result = DB::select($sql, [$stage_id]);
            foreach ($result as &$data) {
                $media_id = $data->media_id;
                $data->expect_money = DB::table('kdgx_partner_charge_pair')->where(array(
                        ['stage_id', $stage_id],
                        ['media_id', $media_id],
                        ['sex', 1]
                    ))->whereBetween('state', [100, 199])->count() * 2.5;
                $data->expect_money += DB::table('kdgx_partner_charge_pair')->where(array(
                        ['stage_id', $stage_id],
                        ['media_id', $media_id],
                        ['sex', 2]
                    ))->whereBetween('state', [100, 199])->count() * 3.5;

                $data->expect_money += DB::table('kdgx_partner_charge_pair')->where(array(
                        ['stage_id', $stage_id],
                        ['media_id', $media_id]
                    ))->whereBetween('state', [300, 399])->count() * 0.5;

                $data->expect_money += DB::table('kdgx_partner_charge_pair')->where(array(
                        ['stage_id', $stage_id],
                        ['media_id', $media_id],
                        ['sex', 1]
                    ))->whereBetween('state', [400, 499])->count() * 2.5;
                $data->expect_money += DB::table('kdgx_partner_charge_pair')->where(array(
                        ['stage_id', $stage_id],
                        ['media_id', $media_id],
                        ['sex', 2]
                    ))->whereBetween('state', [400, 499])->count() * 3.5;

                $data->expect_money += DB::table('kdgx_partner_charge_pair')->where(array(
                        ['stage_id', $stage_id],
                        ['media_id', $media_id],
                    ))->whereBetween('state', [500, 599])->count() * 0.5;

                $data->expect_money += DB::table('kdgx_partner_charge_pair')->where(array(
                        ['stage_id', $stage_id],
                        ['media_id', $media_id],
                    ))->whereBetween('state', [600, 699])->count() * 0.5;

                $data->expect_money += \DB::table('kdgx_partner_charge_pair')->where(array(
                        ['stage_id', $stage_id],
                        ['media_id', $media_id],
                    ))->whereBetween('state', [700, 799])->count() * 0.5;

                $data->expect_money += \DB::table('kdgx_partner_charge_pair')->where(array(
                        ['stage_id', $stage_id],
                        ['media_id', $media_id],
                        ['sex', 1]
                    ))->whereBetween('state', [800, 899])->count() * 2.5;
                $data->expect_money += \DB::table('kdgx_partner_charge_pair')->where(array(
                        ['stage_id', $stage_id],
                        ['media_id', $media_id],
                        ['sex', 2]
                    ))->whereBetween('state', [800, 899])->count() * 3.5;
            }
        } else {
            $sql = "select count(*) as count, media_id from kdgx_partner_charge_pair where `state` between 100 and 899 and `media_id` not in ({$hidesql}'+') GROUP BY `media_id` ORDER BY count desc";
            $result = \DB::select($sql);
        }
        usort($result, function ($x, $y) {
            if ($x->count > $y->count) {
                return false;
            } else {
                return true;
            }
        });
        return $result;
    }


    /**
     * 当前的进度
     * @return int
     */
    public function progress()
    {
        if (empty($this->id)) {
            return self::NOT_ENROLL;
        }
        $activity = ActivityModel::find($this->stage_id);
//        dump($activity->getProgress());
        // 报名中
        if ($activity->getProgress() == ActivityModel::ENROLLING) {
            return self::ENROLLING;
        }
        // 匹配中
        if ($activity->getProgress() == ActivityModel::MATCHING) {
            return self::MATCHING;
        }
        if ($this->state >= 0 && $this->state < 100) {
            return self::INVALID;
        }
        if ($this->state >= 300 && $this->state < 400) {
            return self::MATCH_FAIL;
        }
        if ($this->state >= 500 && $this->state < 600) {
            return self::DISSATISFIED;
        }
        if ($this->state >= 600 && $this->state < 700) {
            return self::REFUND;
        }
        if ($this->state >= 700 && $this->state < 800) {
            return self::NEXT_ISSUE;
        }
        if ($this->state >= 900 && $this->state < 1000) {
            return self::INVALID;
        }
        if ($this->state >= 1000 && $this->state < 1100) {
            return self::NEXT_ISSUE;
        }


        // 40x or 80x
        if (
            ($this->state >= 400 && $this->state < 500)
            or
            ($this->state >= 800 && $this->state < 900)
        ) {
            // 确认期间
            if ($activity->getProgress() == ActivityModel::CONFIRMING) {
                // 另一半
                $other = PairModel::find($this->assoc_id);
                if ($this->confirm == 0) {
                    return self::MATCH_SUCCESS;
                }
                if ($this->confirm < 0) {
                    return self::REMATCHING;
                }
                if ($this->confirm > 0 && $other->confirm == 0) {
                    return self::MATCH_SUCCESS;
                }
                if ($this->confirm > 0 && $other->confirm < 0) {
                    return self::REMATCHING;
                }
                if ($this->confirm > 0 && $other->confirm > 0) {
                    return self::CHATTING;
                }
            }


            # 重匹期间
            if ($activity->getProgress() == ActivityModel::REMATCHING) {
                # 我操作重匹 （一定是重匹中）
                if ($this->confirm < 0) {
                    return self::REMATCHING;
                }
                // 重匹期间有last_accoc（一定是重匹中）
                if ($this->last_assoc) {
                    return self::REMATCHING;
                }

                # 我操作确认
                if ($this->confirm > 0) {
                    $other = PairModel::find($this->assoc_id);
                    if ($other->confirm > 0) {
                        return self::CHATTING;
                    } else {
                        return self::REMATCHING;
                    }
                }

                # 我未操作时
                if ($this->confirm == 0) {
                    if (!$this->assoc_id) {
                        return self::MISS;
                    }
                    $other = PairModel::find($this->assoc_id);
                    // 判断对方是否操作
                    if ($other->confirm == 0) {
                        return self::STALEMATE;
                    } else {
                        return self::MISS;
                    }
                }
            }
            // 活动期间
            if ($activity->getProgress() == ActivityModel::CHATTING) {
                if ($this->assoc_id) {
                    return self::CHATTING;
                } else {
                    # 我未操作时
                    if ($this->confirm == 0) {
                        return self::MISS;
                    } else {
                        return self::REMATCH_FAIL;
                    }
                }
            }
            // 活动结束后
            if ($activity->getProgress() == ActivityModel::END) {
                if ($this->assoc_id) {
                    return self::END;
                } else {
                    # 我未操作时
                    if ($this->confirm == 0) {
                        return self::MISS;
                    } else {
                        return self::REMATCH_FAIL;
                    }
                }
            }
        }
    }


    /**
     * 未报名
     */
    public const NOT_ENROLL = 0;

    /**
     * 报名中
     */
    public const ENROLLING = 1;

    /**
     * 匹配中
     */
    public const MATCHING = 2;

    /**
     * 匹配失败
     */
    public const MATCH_FAIL = 3;

    /**
     * 匹配成功&待确认
     */
    public const MATCH_SUCCESS = 4;


    /**
     * 不满意
     */
    public const DISSATISFIED = 5;

    /**
     * 已退款
     */
    public const REFUND = 6;

    /**
     * 报名下一期
     */
    public const NEXT_ISSUE = 7;

    /**
     * 活动已完成
     */
    public const END = 8;

    /**
     * 报名失效
     */
    public const INVALID = 9;


    /**
     * 重匹中
     */
    public const REMATCHING = 10;

    /**
     * 重匹失败
     */
    public const REMATCH_FAIL = 11;


    /**
     * 匹配成功&活动中
     */
    public const CHATTING = 12;


    /**
     * 错过时间
     */
    public const MISS = 13;

    /**
     * 双方未操作
     */
    public const STALEMATE = 14;
}
