<?php

namespace App\Services\User;

use App\Exceptions\AlertException;
use App\Models\Common\FormidModel;
use App\Models\Deed\FriendsModel;
use App\Models\Deed\InvitationCardModel;
use App\Models\Log\ClickUserHomeLogModel;
use App\Models\Log\FeedLogModel;
use App\Models\PartnerModel;
use App\Models\PraiseModel;
use App\Models\User\AuthKey;
use App\Models\User\FeedType4RemindModel;
use App\Models\User\Openid;
use App\Models\User\SuperLikeModel;
use App\Models\User\UserModel;
use App\Models\User\UserStateModel;
use App\Models\User\UserSysTagModel;
use App\Services\Service;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Redis;

/**
 * 用户主页
 */
class HomeService extends Service
{
    /**
     * 喜欢我的列表
     * @param  int  $uid
     * @param  array  $pages
     * @return array
     * @deprecated
     */
    public function likeMeList(int $uid, array $pages): array
    {
        $user = UserModel::findOrFail($uid);
        $datas = collect(array());
        $total = 0;
        if ($user->partner_id > 0) {
            $total = DB::table('kdgx_partner_charge_praise as praise')
                ->join('kdgx_partner_charge_partner as partner', 'praise.uid', '=', 'partner.uid')
                ->where(array(['praise.partner_id', $user->partner_id], ['praise.type', 1]))
                ->count();
            $datas = DB::table('kdgx_partner_charge_praise as praise')
                ->join('kdgx_partner_charge_partner as partner', 'praise.uid', '=', 'partner.uid')
                ->selectRaw("praise.lock, praise.read, praise.id as praise_id, partner.id, partner.uid, partner.photo_src, partner.check_photo, 
                partner.voice, partner.voice_check, partner.photo_1, partner.photo_1_check, partner.photo_2, partner.photo_2_check, partner.photo_3, partner.photo_3_check, partner.photo_4, partner.photo_4_check,
                partner.black_at,partner.is_sell,partner.praises, praise.updated_at")
                ->where(array(['praise.partner_id', $user->partner_id], ['partner.is_sell', 1], ['praise.type', 1]))
                ->skip(($pages['page'] - 1) * $pages['limit'])->take($pages['limit'])
                ->orderBy('praise.updated_at', 'desc')->get();
        }

        $lookmeuid = array();
        if (!$datas->isEmpty() && $pages['page'] * $pages['limit'] >= $total) {
            if (PraiseModel::where([['uid', $uid], ['partner_id', 1], ['type', 1]])->exists()) {
                $partner = PartnerModel::findOrFail(
                    1,
                    ['id', 'uid', 'photo_src', 'voice', 'photo_1', 'photo_2', 'photo_3', 'photo_4']
                );
                $datas->push($partner);
            }
            if (PraiseModel::where([['uid', $uid], ['partner_id', 2], ['type', 1]])->exists()) {
                $partner = PartnerModel::findOrFail(
                    2,
                    ['id', 'uid', 'photo_src', 'voice', 'photo_1', 'photo_2', 'photo_3', 'photo_4']
                );
                $datas->push($partner);
            }
        }
        foreach ($datas as &$data) {
            $data->photo_src = empty($data->photo_src) ? null : "https://oss.pocketuniversity.cn{$data->photo_src}";
            $data->photo_1 = empty($data->photo_1) ? null : "https://oss.pocketuniversity.cn{$data->photo_1}";
            $data->photo_2 = empty($data->photo_2) ? null : "https://oss.pocketuniversity.cn{$data->photo_2}";
            $data->photo_3 = empty($data->photo_3) ? null : "https://oss.pocketuniversity.cn{$data->photo_3}";
            $data->photo_4 = empty($data->photo_4) ? null : "https://oss.pocketuniversity.cn{$data->photo_4}";
            if (1 != $data->check_photo) {
                unset($data->photo_src);
            }
            if (1 != $data->photo_1_check) {
                unset($data->photo_1);
            }
            if (1 != $data->photo_2_check) {
                unset($data->photo_2);
            }
            if (1 != $data->photo_3_check) {
                unset($data->photo_3);
            }
            if (1 != $data->photo_4_check) {
                unset($data->photo_4);
            }
            if (1 != $data->voice_check) {
                unset($data->voice);
            }
            unset($data->check_photo);
            unset($data->photo_1_check);
            unset($data->photo_2_check);
            unset($data->photo_3_check);
            unset($data->photo_4_check);
            unset($data->voice_check);
            $data->user = UserModel::find($data->uid, [
                'uid',
                'nickname',
                'headimgurl',
                'sex',
                'be_vip_at',
                'supvip_endat',
                'identity_auth',
                'wx_auth',
                'login_at',
            ]);
            if (FriendsModel::where([['uid', $data->uid], ['friend_uid', $uid]])->exists()) {
                $data->lock = 100;
            }
            $data->remind = FeedType4RemindModel::where([
                'uid' => $uid,
                'is_remind_uid' => $data->uid,
                'type' => 3,
            ])->exists() ? true : false;
            $data->superlike = SuperLikeModel::where([
                ['uid', $data->uid],
                ['partner_id', $user->partner_id],
            ])->exists() ? true : false;
            array_push($lookmeuid, $data->uid);
        }
        $unreads = DB::table('kdgx_partner_charge_praise as praise')
            ->join('kdgx_partner_charge_partner as partner', 'praise.uid', '=', 'partner.uid')
            ->selectRaw("partner.id")
            ->where(array(['praise.partner_id', $user->partner_id], ['praise.type', 1], ['read', 0]))
            ->get()
            ->pluck('id');

        PraiseModel::where('partner_id', $user->partner_id)->update(['read' => 1]);

        return array(
            'pages' => array(
                'page' => $pages['page'],
                'limit' => $pages['limit'],
                'total' => $total + 1,
            ),
            'tag_count' => count($unreads),
            'list' => $datas,
            'unread_list' => $unreads,
        );
    }


    /**
     * 我喜欢的列表
     * @param  int  $uid
     * @param  array  $pages
     * @return array
     * @deprecated
     */
    public function likeList(int $uid, array $pages): array
    {
        $total = DB::table('kdgx_partner_charge_praise as praise')
            ->join('kdgx_partner_charge_partner as partner', 'praise.partner_id', '=', 'partner.id')
            ->where([['praise.uid', $uid], ['type', 1], ['is_sell', 1]])->count();
        $datas = DB::table('kdgx_partner_charge_praise as praise')
            ->join('kdgx_partner_charge_partner as partner', 'praise.partner_id', '=', 'partner.id')
            ->select(DB::raw("partner.id, praise.type, praise.id as praise_id, partner.uid, partner.school, partner.sex, 
                partner.age, partner.star, partner.photo_src, partner.check_photo, partner.voice, partner.voice_check,
                partner.photo_1, partner.photo_1_check, partner.photo_2, partner.photo_2_check, partner.photo_3, partner.photo_3_check,
                partner.photo_4, partner.photo_4_check, partner.is_sell, partner.black_at, partner.praises, praise.updated_at"))->where([
                ['praise.uid', $uid],
                ['type', 1],
                ['is_sell', 1],
                ['is_hide', 0],
            ])
            ->skip(($pages['page'] - 1) * $pages['limit'])->take($pages['limit'])->orderBy(
                'praise.updated_at',
                'desc'
            )->get();
        foreach ($datas as &$data) {
            $data->photo_src = "https://oss.pocketuniversity.cn{$data->photo_src}";
            $data->photo_1 = "https://oss.pocketuniversity.cn{$data->photo_1}";
            $data->photo_4 = "https://oss.pocketuniversity.cn{$data->photo_4}";
            $data->photo_2 = "https://oss.pocketuniversity.cn{$data->photo_2}";
            $data->photo_3 = "https://oss.pocketuniversity.cn{$data->photo_3}";
            if (1 != $data->check_photo) {
                unset($data->photo_src);
            }
            if (1 != $data->photo_1_check) {
                unset($data->photo_1);
            }
            if (1 != $data->photo_2_check) {
                unset($data->photo_2);
            }
            if (1 != $data->photo_3_check) {
                unset($data->photo_3);
            }
            if (1 != $data->photo_4_check) {
                unset($data->photo_4);
            }
            if (1 != $data->voice_check) {
                unset($data->voice);
            }
            unset($data->check_photo);
            unset($data->photo_1_check);
            unset($data->photo_2_check);
            unset($data->photo_3_check);
            unset($data->photo_4_check);
            unset($data->voice_check);
            try {
                $data->user = UserModel::findOrFail($data->uid, [
                    'uid',
                    'headimgurl',
                    'nickname',
                    'login_at',
                    'age',
                    'star',
                    'introduce',
                    'expect',
                    'height',
                    'sex',
                    'school',
                    'address',
                    'home',
                    'tag_1',
                    'tag_2',
                    'tag_3',
                    'tag_4',
                    'be_vip_at',
                    'supvip_endat',
                    'identity_auth',
                    'wx_auth',
                ]);
                $data->user->invite_cnt = InvitationCardModel::where('invite_uid', $data->uid)->count();
            } catch (\Exception $e) {
                $data->user = null;
            }
            $data->is_self = ($uid == $data->uid) ? 1 : 0;
            $data->remind = FeedType4RemindModel::where([
                'uid' => $uid,
                'is_remind_uid' => $data->uid,
                'type' => 2,
            ])->exists() ? true : false;
        }

        return array(
            'pages' => array(
                'page' => $pages['page'],
                'limit' => $pages['limit'],
                'total' => $total,
            ),
            'list' => $datas,
        );
    }

    /**
     * 看过我的人数
     * @param  int  $uid
     * @return int
     */
    public function lastLookMe(int $uid)
    {
        $cnt = ClickUserHomeLogModel::where([['home_uid', $uid], ['into_at', '>', time() - 86400 * 7]])->count();
        $cnt = $cnt + 3;

        return $cnt;
    }

    /**
     * 获取未解锁的「心动我的」卡片数量
     * @param  int  $uid
     * @return mixed
     */
    public function unLookLikemeCnt(int $uid)
    {
        $user = UserModel::findOrFail($uid);
        $cnt = DB::table('kdgx_partner_charge_praise as praise')
            ->join('kdgx_partner_charge_partner as partner', 'praise.uid', '=', 'partner.uid')
            ->where(array(['praise.partner_id', $user->partner_id], ['praise.type', 1], ['lock', '<', 100]))
            ->count();
        $datas = DB::table('kdgx_partner_charge_praise as praise')
            ->join('kdgx_partner_charge_partner as partner', 'praise.uid', '=', 'partner.uid')
            ->selectRaw("praise.uid")
            ->where(array(['praise.partner_id', $user->partner_id], ['praise.type', 1], ['lock', '<', 100]))
            ->take(3)->orderBy('praise.updated_at', 'desc')->get();
        $users = UserModel::whereIn('uid', collect($datas)->pluck('uid'))->get(['uid', 'headimgurl', 'nickname']);

        return array(
            'users' => $users,
            'cnt' => $cnt,
        );
    }

    /**
     *  获取某人最近七天的热度
     * @param  int  $uid
     * @return array
     */
    public function getLast7dayHot(int $uid)
    {
        $user = UserModel::findOrFail($uid);
        $start_at = mktime(0, 0, 0);
        $result = array();
        for ($i = 0; $i < 7; $i++) {
            if (0 == $user->partner_id) {
                $inviteme = 0;
                $thumbme = 0;
                $feedme = 0;
            } else {
                $inviteme = InvitationCardModel::where('invite_uid', $uid)->whereBetween(
                    'created_at',
                    [$start_at, $start_at + 86400]
                )->count();
                $thumbme = PraiseModel::where('partner_id', $user->partner_id)->whereBetween(
                    'created_at',
                    [$start_at, $start_at + 86400]
                )->count();
                $feedme = FeedLogModel::where('partner_id', $user->partner_id)->whereBetween(
                    'created_at',
                    [$start_at, $start_at + 86400]
                )->count();
            }
            $result[] = [
                'date' => date('m-d', $start_at),
                'inviteme' => $inviteme,
                'thumbme' => $thumbme,
                'feedme' => $feedme,
            ];
            $start_at -= 86400;
        }

        return $result;
    }

    public function popularityExpire()
    {
        $start = time();
        $ns = new \App\Services\Share\NoticeService();
        $flowers = UserSysTagModel::whereBetween('popularity_flower_end_at', [$start, $start + 600])->get(['uid']);
        foreach ($flowers as $flower) {
            try {
                if (Redis::exists("popularity:flower:expire:{$flower->uid}")) {
                    continue;
                }
                $ns->popularityFlawerExpire($flower->uid);
                Redis::setex("popularity:flower:expire:{$flower->uid}", 1200, $flower->uid);
            } catch (\Exception $exception) {
                dump($exception);
            }
        }
        $shares = UserSysTagModel::whereBetween('popularity_share_end_at', [$start, $start + 600])->get(['uid']);
        foreach ($shares as $share) {
            try {
                if (Redis::exists("popularity:share:expire:{$share->uid}")) {
                    continue;
                }
                $ns->popularitySharehelpExpire($share->uid);
                Redis::setex("popularity:share:expire:{$share->uid}", 1200, $share->uid);
            } catch (\Exception $exception) {
                dump($exception);
            }
        }
    }

    public function logTodayPopularity()
    {
        $partners = PartnerModel::where([['is_sell', 1], ['base_score', '>', 0]])->get();
        $total = $partners->count();
        $datas = array();
        foreach ($partners as $partner) {
            try {
                $user = UserModel::findOrFail($partner->uid);
                $pdata = $this->getPopularity($partner->uid);
                $tmp = array(
                    'created_at' => time(),
                    'uid' => $partner->uid,
                    'sex' => $user->sex,
                    'date' => date('Y-m-d'),
                    'popularity' => $pdata['base'],
                    'location' => $user->location,
                );
                $datas[] = $tmp;
            } catch (\Exception $exception) {
            }
        }
        usort($datas, function ($v1, $v2) {
            return ($v1['popularity'] < $v2['popularity']) ? -1 : 1;
        });

        $i = 1;
        $popularity = 0;
        $add = array();
        foreach ($datas as &$data) {
            $i++;
            $add[] = $data;
            if (0 == $i % 100) {
                DB::table("kdgx_fpdx_populary_log")->insert($add);
                $add = array();
            }
        }
    }

    // 增加人气值时间到期通知

    /**
     * 获取当前人气值
     * @param  int  $uid
     * @return array
     * @throws AlertException
     */
    public function getPopularity(int $uid)
    {
        $user = UserModel::findOrFail($uid);
        if (0 == $user->partner_id) {
            throw new AlertException("还未生成卡片", 201);
        }
        /** @var PartnerModel $partner */
        $partner = PartnerModel::findOrFail($user->partner_id);
        if (1 != $partner->is_sell) {
            PartnerModel::where('id', $user->partner_id)->update(['base_score' => -1]);
            throw new AlertException("{$user->partner_id}:卡片未上架", 202);
        }

        $canNotice = false;
        // 是否关注fpdx或可以分小程序模板消息
        if (Openid::isSubscribe($uid, "gh_b598cb7474d8")) {
            $canNotice = true;
        }
        if (
            FormidModel::where([
            ['uid', $uid],
            ['public_id', config('miniprogram.public_id')],
            ['state', 0],
            ['created_at', '>', time() - 86400 * 5],
            ])->exists()
        ) {
            $canNotice = true;
        }

        try {
            $auth = AuthKey::where(array(['uid', $uid], ['auth_type', config("qqprogram.app_id")]))->firstOrFail();
            if (
                \App\Models\QQ\FormIdModel::where(array(
                ['openid', $auth->auth_key],
                ['created_at', '>', time() - 6 * 86400],
                ['send_at', 0],
                ))->exists()
            ) {
                $canNotice = true;
            }
        } catch (\Exception $exception) {
        }

        if (!$canNotice) {
            PartnerModel::where('id', $user->partner_id)->update(['base_score' => -1]);
            throw new AlertException("请关注公众号", 203);
        }

        if ($partner->base_score < 0) {
            $partner->base_score = $this->calcBaseScore($partner);
            $partner->save();
        }

        // 基础人气值
        switch ($partner->base_score) {
            case $partner->base_score >= 0 && $partner->base_score < 40:
                $base = 0 + (100 / 40) * ($partner->base_score - 0);
                break;
            case $partner->base_score >= 40 && $partner->base_score < 50:
                $base = 100 + (100 / 10) * ($partner->base_score - 40);
                break;
            case $partner->base_score >= 50 && $partner->base_score < 60:
                $base = 200 + (100 / 10) * ($partner->base_score - 50);
                break;
            case $partner->base_score >= 60 && $partner->base_score < 70:
                $base = 300 + (200 / 10) * ($partner->base_score - 60);
                break;
            case $partner->base_score >= 70 && $partner->base_score < 90:
                $base = 500 + (300 / 10) * ($partner->base_score - 70);
                break;
            case $partner->base_score >= 90:
                $base = 800 + (400 / 10) * ($partner->base_score - 90);
                break;
            default:
                throw new AlertException("人气值生成中", 204);
        }
        $popularity = $base;
        // 附加分 = 好友助力+消耗小fa+超级会员
        $systag = UserSysTagModel::firstOrCreate(
            ['uid' => $user->uid],
            ['uid' => $user->uid, 'popularity_share_end_at' => 0]
        );
        $systag->popularity_share_end_at > time() && $popularity += 100;
        $popularity_sign_end_at = intval(UserStateModel::get($user->uid, "popularity_sign_end_at"));
        $popularity_sign_end_at > time() && $popularity += 100;
        $user->supvip_endat > time() && $popularity += 100;

        return array(
            'popularity_share_end_at' => $systag->popularity_share_end_at,
            'popularity_sign_end_at' => $popularity_sign_end_at,
            'popularity_supvip_endat' => $user->supvip_endat,
            'popularity' => $popularity,
            'base' => $base,
        );
    }

    // 存储用户今日人气值

    /**
     * 估算基础分
     * @param  PartnerModel  $partner
     * @return float
     */
    private function calcBaseScore($partner)
    {
        $DT1 = -0.004;      // 最近更新加分衰变率1
        $DT2 = -0.01;          // 赞数衰变率2*z(x)
        $DT3 = -0.00002;    // 发布时间加分衰变率3
        $DT4 = -0.003;      // 最近上线加分衰变率4
        $params = Redis::hgetAll("msy-feed-params");
        if (empty($params)) {
            $params = array(
                'pt1' => 0.1,
                'pz' => 0.3,
                'pg' => 0.3,
                'pp' => 0.1,
                'pd' => 0.15,
                'pt3' => 0.1,
            );
        }
        // 最近更新时间
        $t1 = (time() - $partner->update_at) / 3600;
        // 0 == $partner->update_at && $t1 = (time() - $partner->created_at) / 3600;
        // 点赞数
        $z = $partner->praises + 1;
        // 曝光量
        $b = $partner->feed_cnt + 2;
        if ($b < $z) {
            $b = $z * 2;
        }
        // 是否有语音
        $p = 60;
        empty($partner->voice) && $p = 100;
        // 图片数量
        $d = 50;
        empty($partner->photo_src) && $d += 10;
        empty($partner->photo_1) && $d += 10;
        empty($partner->photo_2) && $d += 10;
        empty($partner->photo_3) && $d += 10;
        empty($partner->photo_4) && $d += 10;

        // 最近登录时间
        $t2 = (time() - $partner->login_at) / 3600;

        // 发布时间
        $t3 = (time() - $partner->upload_at) / 3600;
        0 == $partner->upload_at && $t3 = (time() - $partner->created_at) / 3600;

        $score1 = 100 * exp($DT1 * $t1) * $params['pt1'] ?? 0.1;
        $score2 = ($z / $b) * 100 * $params['pz'] ?? 0.5;
        $score3 = 0.2 * $params['pg'] ?? 0.3;
        $score4 = $p * $params['pp'] ?? 0.1;
        $score5 = $d * $params['pd'] ?? 0.15;
        $score6 = 100 * exp($DT3 * $t3) * $params['pt3'] ?? 0.1;
        $base_score = round(($score1 + $score2 + $score3 + $score4 + $score5 + $score6) * exp($DT4 * $t2));

        return $base_score;
    }

    /**
     * 访客数据概览
     * @param  int  $uid
     * @return array
     */
    public function getVisitorOverView(int $uid)
    {
        $user = UserModel::findOrFail($uid);
        $today = $feedme = FeedLogModel::where('partner_id', $user->partner_id)->where(
            'created_at',
            '>',
            mktime(0, 0, 0)
        )->select(DB::raw("count(DISTINCT(`uid`)) as count"))->first()->toArray()['count'];

        $month = $feedme = FeedLogModel::where('partner_id', $user->partner_id)->where(
            'created_at',
            '>',
            mktime(0, 0, 0, date('m'), 1, date('Y'))
        )->count();

        $uids = PraiseModel::where('partner_id', $user->partner_id)->get()->pluck('uid')->toArray();
        $users = json_decode(Redis::get("fpdx:user:visitoroverview:feedusers:{$uid}"), true);
        if (!$users) {
            $feeds = FeedLogModel::whereNotIn('kdgx_fpdx_feed_log.uid', $uids)->join(
                'kdgx_partner_charge_partner',
                'kdgx_partner_charge_partner.uid',
                '=',
                'kdgx_fpdx_feed_log.uid'
            )->where([
                ['partner_id', $user->partner_id],
                ['is_sell', 1],
            ])
                ->select(DB::raw("kdgx_fpdx_feed_log.uid,kdgx_fpdx_feed_log.created_at,count(*) as count"))
                ->orderBy('kdgx_fpdx_feed_log.created_at', 'desc')->take(4)->get();
            $users = UserModel::whereIn('uid', $feeds->pluck('uid')->toArray())->get(['uid', 'nickname', 'headimgurl']);
            Redis::setex("fpdx:user:visitoroverview:feedusers:{$uid}", 86400, json_encode($users));
        }
        try {
            $pdata = $this->getPopularity($uid);
            $popularity = $pdata['popularity'];
        } catch (\Exception $exception) {
            $popularity = 0;
        }

        return compact('today', 'month', 'users', 'popularity');
    }

    /**
     * 访客数据列表
     * @param  int  $uid
     * @param  array  $pages
     * @return array
     * @deprecated
     */
    public function getVisitorList(int $uid, array $pages)
    {
        $user = UserModel::findOrFail($uid);
        $uids = PraiseModel::where('partner_id', $user->partner_id)->get()->pluck('uid')->toArray();

        $total = FeedLogModel::whereHas('uidPartner', function (/** @var Builder $query */ $query) {
            $query->where('is_sell', 1);
        })
            ->whereNotIn('uid', $uids)
            ->where('partner_id', $user->partner_id)
            ->distinct()
            ->count();
        $innerDB = FeedLogModel::whereHas('uidPartner', function (/** @var Builder $query */ $query) {
            $query->where('is_sell', 1);
        })
            ->whereNotIn('uid', $uids)
            ->where('partner_id', $user->partner_id)
            ->orderBy('id');
        $innerSql = $innerDB->toSql();
        $innerBind = $innerDB->getBindings();
        $offset = ($pages['page'] - 1) * $pages['limit'];
        $sql = "SELECT tab.uid, tab.created_at, count(*) as count FROM($innerSql) as tab group by `uid` ORDER BY `created_at` DESC limit {$pages['limit']} offset $offset";
        $feeds = Collection::make(DB::select($sql, $innerBind));
        $users = UserModel::whereIn('uid', $feeds->pluck('uid')->toArray())->get([
            'uid',
            'nickname',
            'headimgurl',
            'sex',
        ])->toArray();
        $users = array_combine(array_column($users, 'uid'), $users);

        foreach ($feeds as &$feed) {
            $feed->users = $users[$feed->uid];
        }

        return [
            'total' => $total,
            'page' => $pages['page'],
            'limit' => $pages['limit'],
            'list' => $feeds,
        ];
    }
}
