<?php

namespace App\Services\User;

use AlicFeng\IdentityCard\Birthday;
use App\Events\AuditMedia;
use App\Exceptions\AlertException;
use App\Jobs\AppearanceUserJob;
use App\Jobs\FaceVerifyJob;
use App\Jobs\GrowingIO\UserInfoReportJob;
use App\Jobs\GrowingIO\UserVipReportJob;
use App\Models\Common\TokenModel;
use App\Models\Fpdx\ActivityModel;
use App\Models\Fpdx\PairModel;
use App\Models\Log\UserPhotoLogModel;
use App\Models\Log\VipLogModel;
use App\Models\PartnerModel;
use App\Models\TagModel;
use App\Models\User\AuthKey;
use App\Models\User\FaceMatchModel;
use App\Models\User\Openid;
use App\Models\User\UserModel;
use App\Models\Wxkf\UserInfoModel;
use App\Services\Partner\NoticeService;
use App\Services\Service;
use App\Services\Vendor\BaiduAi\FaceService;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Redis;
use Sentry\SentryLaravel\SentryFacade;

/**
 * 用户信息配置服务
 */
class ProfileService extends Service
{

    /**
     * 更新用户基础配置信息
     * @param  UserModel  $user
     * @param  array  $data
     * @return void
     * @throws AlertException
     * @throws \AlicFeng\IdentityCard\Exception\BirthdayException
     */
    public function updateUser($user, array $data)
    {
        if (empty($data)) {
            return;
        }

        // 输入检验
        if (array_key_exists('location', $data) && is_array($data['location'])) {
            $data['location'] = "";
        }
        if (
            (isset($data['photo_src'])
                && empty($data['photo_src']))
            || (isset($data['photo_1'])
                && empty($data['photo_1']))
            || (isset($data['photo_2'])
                && empty($data['photo_2']))
            || (isset($data['photo_3'])
                && empty($data['photo_3']))
            || (isset($data['photo_4'])
                && empty($data['photo_4']))
        ) {
            throw new AlertException("照片非法删除", 103);
        }

        // 判断是否在活动期间更新敏感信息
        $ings = ActivityModel::getActivitys();
        if ($ings['ing'] != -1) {
            $exists = PairModel::where([
                ['uid', $user->uid],
                ['stage_id', $ings['ing']],
            ])->whereNotNull('assoc_id')->exists();
            $week4_18 = mktime(18, 0, 0, date('m'), date('d') - date('w') + 4, date('Y'));
            $week5_10 = mktime(10, 0, 0, date('m'), date('d') - date('w') + 5, date('Y'));
            if (
                (time() < $week4_18
                    || time() > $week5_10)
                && $exists
                && (array_key_exists('sex', $data)
                    || array_key_exists('sxo', $data)
                    || array_key_exists('age', $data)
                    || array_key_exists('address', $data)
                    || array_key_exists('home', $data))
            ) {
                throw new AlertException("72小时活动进行或预匹配期间，不允许修改敏感信息", 102);
            }
        }

        // 提交素材审核事件
        $this->auditDiscribute($user, $data);

        // 是否修改了认证消息
        if (!empty($user->identity_auth)) {
            $identitys = explode(',', $user->identity_auth);
            $ids = array_flip($identitys);
            foreach ($identitys as $identity) {
                if (array_key_exists($identity, $data)) {
                    unset($ids[$identity]);
                }
            }
            $identitys = array_flip($ids);
            $user->identity_auth = implode(",", $identitys);
        }

        if (array_key_exists('weixin', $data) && $user->weixin != $data['weixin']) {
            $data['wx_auth'] = 0;
        }

        isset($data['photo_src']) && UserPhotoLogModel::create(['uid' => $user->uid, 'src' => $data['photo_src']]);
        isset($data['photo_1']) && UserPhotoLogModel::create(['uid' => $user->uid, 'src' => $data['photo_1']]);
        isset($data['photo_2']) && UserPhotoLogModel::create(['uid' => $user->uid, 'src' => $data['photo_2']]);
        isset($data['photo_3']) && UserPhotoLogModel::create(['uid' => $user->uid, 'src' => $data['photo_3']]);
        isset($data['photo_4']) && UserPhotoLogModel::create(['uid' => $user->uid, 'src' => $data['photo_4']]);

        $age = $data['age'] ?? $user->age ?? null;

        if ($age) {
            $data['star'] = Birthday::star(strtotime($age));
        }

        $user->fill($data);

        // 更改定位后处理feed流池
        if ((isset($data['lat']) && isset($data['lng']) && isset($data['location']))) {
            $rank1 = Redis::zrank("fpdx:user:locations:sell:girl", $user->uid);
            $rank2 = Redis::zrank("fpdx:user:locations:sell:boy", $user->uid);
            if ($rank1 || $rank2) {
                if ($user->sex == 1) {
                    Redis::zrem("fpdx:user:locations:sell:girl", $user->uid);
                    Redis::geoAdd("fpdx:user:locations:sell:boy", $user->lng, $user->lat, $user->uid);
                } elseif ($user->sex == 2) {
                    Redis::zrem("fpdx:user:locations:sell:boy", $user->uid);
                    Redis::geoAdd("fpdx:user:locations:sell:girl", $user->lng, $user->lat, $user->uid);
                }
            }
        }

        $user->save();

        $syncFields = array(
            'home',
            'address',
            'sex',
            'photo_src',
            'photo_1',
            'photo_2',
            'photo_3',
            'photo_4',
            'voice',
            'school',
            'subject',
            'grade',
            'height',
            'age',
            'star',
            'introduce',
            'expect',
            'qq',
            'weixin',
            'education',
        );
        if (isset($syncFields['sex'])) {
            dispatch_now(new UserInfoReportJob([
                [
                    'loginUserId' => $user->uid,
                    'gender' => $syncFields['sex'] == 1 ? '男性' : ($syncFields['sex'] == 2 ? '女性' : '未知'),
                ],
            ]));
        }
        // 同步更新卡片
        if (
            isset($user->partner_id) && $user->partner_id > 0 && !empty(array_intersect(
                array_keys($data),
                $syncFields
            ))
        ) {
            $ps = new PartnerService();
            $ps->updatePartner($user->partner_id, $data);
        }
    }

    /**
     * 提交素材审核事件
     * @param $user
     * @param $data
     * @throws AlertException
     */
    private function auditDiscribute($user, $data)
    {
        // 昵称
        if (array_key_exists('nickname', $data) && $data['nickname']) {
            event(new AuditMedia($user->uid, 'text', $data['nickname'], 'nickname', "uid={$user->uid}"));
        }
        // 头像
        if (array_key_exists('headimgurl', $data) && $data['headimgurl']) {
            event(new AuditMedia($user->uid, 'image', $data['headimgurl'], 'headimgurl', "uid={$user->uid}"));
        }
        // 个性签名
        if (array_key_exists('introduce', $data) && $data['introduce']) {
            event(new AuditMedia($user->uid, 'text', $data['introduce'], 'introduce', "uid={$user->uid}"));
        }
    }

    public function toggleTag(int $uid, $tag_id, $group)
    {
        $user = UserModel::findOrFail($uid, ['tag_1', 'tag_2', 'tag_3', 'tag_4']);

        $taged = explode(',', $user->{"tag_{$group}"});
        if (in_array($tag_id, $taged)) {
            $key = array_search($tag_id, $taged);
            array_splice($taged, $key, 1);
            $is_add = false;
        } else {
            array_push($taged, $tag_id);
            $is_add = true;
        }

        UserModel::where('uid', $uid)->update(["tag_{$group}" => ltrim(trim(implode(',', $taged)), ',')]);

        $tagModel = new TagModel();
        $tagModel->flushTagByUser($uid, compact('group', 'tag_id'), $is_add);
    }

    /**
     * 删除照片
     * @param  int  $uid
     * @param  string  $pField
     * @return bool
     * @throws \Exception
     */
    public function deletePhoto(int $uid, string $pField)
    {
        $fields = ['photo_src', 'photo_1', 'photo_2', 'photo_3', 'photo_4'];
        $checks = array(
            'photo_src' => 'check_photo',
            'photo_1' => 'photo_1_check',
            'photo_2' => 'photo_2_check',
            'photo_3' => 'photo_3_check',
            'photo_4' => 'photo_4_check',
        );

        $user = UserModel::findOrFail($uid);
        $identitys = array_flip(explode(',', $user->identity_auth));

        $partner = PartnerModel::find($user->partner_id);

        $photos = array(
            'photo_src' => [
                'field' => 'photo_src',
                'src' => $user->photo_src,
                'check' => $partner->check_photo ?? -1,
                'identity_auth' => isset($identitys['photo_src']) ? true : false,
            ],
            'photo_1' => [
                'field' => 'photo_1',
                'src' => $user->photo_1,
                'check' => $partner->photo_1_check ?? -1,
                'identity_auth' => isset($identitys['photo_1']) ? true : false,
            ],
            'photo_2' => [
                'field' => 'photo_2',
                'src' => $user->photo_2,
                'check' => $partner->photo_2_check ?? -1,
                'identity_auth' => isset($identitys['photo_2']) ? true : false,
            ],
            'photo_3' => [
                'field' => 'photo_3',
                'src' => $user->photo_3,
                'check' => $partner->photo_3_check ?? -1,
                'identity_auth' => isset($identitys['photo_3']) ? true : false,
            ],
            'photo_4' => [
                'field' => 'photo_4',
                'src' => $user->photo_4,
                'check' => $partner->photo_4_check ?? -1,
                'identity_auth' => isset($identitys['photo_4']) ? true : false,
            ],
        );

        unset($photos[$pField]);

        $u_save = $p_save = $u_identity = array();
        foreach ($fields as $field) {
            $photo = array_shift($photos);
            if (is_null($photo)) {
                $u_save[$field] = null;
                $p_save[$field] = null;
                $p_save[$checks[$field]] = -1;
            } else {
                $u_save[$field] = $photo['src'];
                $p_save[$field] = $photo['src'];
                $p_save[$checks[$field]] = $photo['check'];
                if ($photo['identity_auth']) {
                    array_push($u_identity, $field);
                }
            }
        }
        $u_save['identity_auth'] = implode(",", $u_identity);

        DB::beginTransaction();
        try {
            $user->fill($u_save);
            $partner->fill($p_save);
            // 修正上架状态
            if (
                1 == $partner->is_sell
                && (empty($partner->qq)
                    && empty($partner->weixin))
                || !(1 == $partner->check_photo
                    || 1 == $partner->photo_1_check
                    || 1 == $partner->photo_2_check
                    || 1 == $partner->photo_3_check
                    || 1 == $partner->photo_4_check
                    || 1 == $partner->voice_check)
            ) {
                $partner->is_sell = 0;
                Redis::zrem("fpdx:user:locations:sell:boy", $partner->uid);
                Redis::zrem("fpdx:user:locations:sell:girl", $partner->uid);
            }
            // 修正审核状态
            if (
                1 == $partner->is_commit_check
                && 0 != $partner->check_photo
                && 0 != $partner->photo_1_check
                && 0 != $partner->photo_2_check
                && 0 != $partner->photo_3_check
                && 0 != $partner->photo_4_check
                && 0 != $partner->voice_check
            ) {
                $partner->is_commit_check = 0;
            }

            $user->save();
            $partner->save();
            AppearanceUserJob::dispatch($user->uid)->onQueue('{appearance:user}');

            DB::commit();

            return true;
        } catch (\Exception $exception) {
            DB::rollBack();
            throw $exception;
        }
    }

    /**
     * 活体身份认证
     * @param  int  $uid  用户信息
     * @param  string  $face_url  用于活体检测的图片
     * @param  string  $photo_field  用于对比的图片字段
     * @return bool
     * @throws AlertException
     */
    public function fatchmatch(int $uid, string $face_url, string $photo_field)
    {
        $checks = array(
            'photo_src' => 'check_photo',
            'photo_1' => 'photo_1_check',
            'photo_2' => 'photo_2_check',
            'photo_3' => 'photo_3_check',
            'photo_4' => 'photo_4_check',
        );

        $user = UserModel::findOrFail($uid);

        $isfield = true;
        if (in_array($photo_field, ['photo_src', 'photo_1', 'photo_2', 'photo_3', 'photo_4'])) {
            if (empty($user->{$photo_field})) {
                throw new AlertException("没有找到可对比的图片", 101);
            } else {
                $match_photo = "https://oss.pocketuniversity.cn" . $user->{$photo_field};
            }
        } else {
            $isfield = false;
            $match_photo = $photo_field;
        }
        $fs = new FaceService();
        $images = array(
            [
                'image' => $face_url,
                'image_type' => 'URL',
                'face_type' => 'LIVE',
                'quality_control' => 'NONE',
                'liveness_control' => 'LOW',
            ],
            [
                'image' => $match_photo,
                'image_type' => 'URL',
                'face_type' => 'LIVE',
                'quality_control' => 'NONE',
                'liveness_control' => 'NONE',
            ],
        );
        $data = $fs->match($images);
        $fmm = new FaceMatchModel();
        $md = $fmm->fill([
            'uid' => $user->uid,
            'check_img' => $match_photo,
            'match_img' => $face_url,
        ]);
        if ($data['error_code'] == 0) {
            $md->score = $data['result']['score'] ?? 0;
            $md->save();
            if ($data['result']['score'] > 69) {
                if ($isfield) {
                    if (empty($user->identity_auth)) {
                        $user->identity_auth = $photo_field;
                    } else {
                        $identity = explode(',', $user->identity_auth);
                        array_push($identity, $photo_field);
                        $user->identity_auth = implode(",", array_unique($identity));
                    }
                    $user->save();
                    $ns = new NoticeService();
                    if ($user->partner_id > 0) {
                        $ps = new PartnerService();
                        $ps->check($user->partner_id, $checks[$photo_field], 1);
                    }
                    if ($user->wx_auth == 1) {
                        $ns->authenticationSuccess($user->uid);
                    }
                }

                return true;
            } else {
                throw new AlertException("与上传照片相似度低", 101);
            }
        } else {
            if ($isfield && $data['error_code'] == 222204) {
                FaceVerifyJob::dispatch($uid, $face_url, $user->{$photo_field}, 0)->delay(Carbon::now()->addMinutes(1));
                throw new AlertException("图片加载失败，已加入审核队列", 102);
            }
            $md->score = 0;
            $md->save();
            throw new AlertException("请确保光线充足，没有翻拍照片", 101);
        }
    }

    /**
     * 微信认证
     * @param  int  $uid
     * @param  string  $wxid
     * @param  string  $wxkf
     * @return array
     * @throws \Exception
     */
    public function wxverify(int $uid, string $wxid, string $wxkf): array
    {
        $user = UserModel::findOrFail($uid);
        $before_auth = 0;
        $after_auth = 0;
        if ($user->identity_auth && $user->wx_auth == 1) {
            $before_auth = 1;
        }


        if ($user->wx_auth == 1) {
            return array(
                "code" => 200,
                "message" => "验证成功",
            );
        }

        $userinfo = UserInfoModel::where('wxid', $wxid)->first();
        if (collect($userinfo)->isEmpty()) {
            app('sentry')->captureMessage("微信客服验证异常日志:%s", array("无法验证的微信号"), array(
                'level' => 'info',
                'extra' => array(
                    '用户' => $uid,
                    '微信id' => $wxid,
                    '服务微信客服' => $wxkf,
                ),
                'tags' => array(
                    'uid' => $uid,
                ),
            ));

            $kfmsg = array(
                'task_type' => 1,
                'task_dict' => array(
                    'wxid_to' => $wxid,
                    'at_list' => "",
                    'msg_list' => array(
                        [
                            'msg_type' => 1,
                            'msg' => "很抱歉，认证失败。\n\n可能原因：\n\n小遇出错了，暂时没有能够检测到您设置的微信号。\n\n请确保您已设置微信号后，👇点击下方链接提醒小象导帮您人工审核。",
                        ],
                        [
                            'msg_type' => 1,
                            'msg' => "点击链接提醒小遇人工审核\nhttps://m.fenpeiduixiang.com/fpdx-wxauth/artifical.html?wxid={$wxid}&kf={$wxkf}",
                        ],
                    ),
                ),
            );
            Redis::sadd("wehub:task:list:{$wxkf}", [json_encode($kfmsg)]);

            return array(
                "code" => 101,
                "message" => "已提交验证,请耐心等待",
            );
        }

        DB::beginTransaction();
        try {
            // 无微信号且wxid为默认值
            if (empty($userinfo->wx_alias) && preg_match("/^wxid_/", $userinfo->wxid)) {
                $kfmsg = array(
                    'task_type' => 1,
                    'task_dict' => array(
                        'wxid_to' => $wxid,
                        'at_list' => "",
                        'msg_list' => array(
                            [
                                'msg_type' => 1,
                                'msg' => "你还没有设置微信号哦，请在微信客户端设置微信号【点此链接参阅设置方法：https://jingyan.baidu.com/article/a948d6512dd2d50a2dcd2e8a.html】后，向我回复“认证”再试。\n\n若多次尝试失败，可向我回复\"人工认证\"，辅助认证。",
                            ],
                        ),
                    ),
                );
                Redis::sadd("wehub:task:list:{$wxkf}", [json_encode($kfmsg)]);

                return array(
                    "code" => 101,
                    "message" => "已提交验证,请耐心等待",
                );
            }

            // 无微信号且wxid不为默认值
            if (empty($userinfo->wx_alias) && !preg_match("/^wxid_/", $userinfo->wxid)) {
                $wx_alias = $userinfo->wxid;
            } elseif (empty($userinfo->wx_alias)) {
                $kfmsg = array(
                    'task_type' => 1,
                    'task_dict' => array(
                        'wxid_to' => $wxid,
                        'at_list' => "",
                        'msg_list' => array(
                            array(
                                'msg_type' => 1,
                                'msg' => "很抱歉，认证失败。\n\n可能原因：\n\n小遇出错了，暂时没有能够检测到您设置的微信号。\n\n请确保您已设置微信号后，👇点击下方链接提醒小象导帮您人工审核",
                            ),
                            [
                                'msg_type' => 1,
                                'msg' => "点击链接提醒小遇人工审核\nhttps://m.fenpeiduixiang.com/fpdx-wxauth/artifical.html?wxid={$wxid}&kf={$wxkf}",
                            ],
                        ),
                    ),
                );
                Redis::sadd("wehub:task:list:{$wxkf}", [json_encode($kfmsg)]);

                return array(
                    "code" => 101,
                    "message" => "已提交验证,请耐心等待",
                );
            } else {
                $wx_alias = $userinfo->wx_alias;
            }

            $user->wx_auth = 1;
            $user->weixin = $wx_alias;
            $user->save();
            if ($user->partner_id > 0) {
                $ps = new PartnerService();
                $ps->updatePartner($user->partner_id, ['weixin' => $wx_alias]);
            }

            DB::commit();

            (new NoticeService())->partnerCheckLog($user->uid, $user->partner_id, 'weixin', 1, 1);
            if ($user->identity_auth && $user->wx_auth == 1) {
                $after_auth = 1;
            }

            $kfmsg = array(
                'task_type' => 1,
                'task_dict' => array(
                    'wxid_to' => $wxid,
                    'at_list' => "",
                    'msg_list' => array(
                        [
                            'msg_type' => 1,
                            'msg' => "恭喜你已完成微信认证，现在你可以回到小程序向喜欢的ta发起“心动邀请”啦。补全信息会帮助认识彼此，同时将获得更高的曝光度。\n\n小遇祝你早日脱单哦！加油~\n\n👇👇👇👇👇",
                        ],
                        [
                            'msg_type' => 3,
                            'msg' => "https://oss.pocketuniversity.cn/media/2019-01-09/5c35d017d6771.png",
                        ],
                    ),
                ),
            );
            Redis::sadd("wehub:task:list:{$wxkf}", [json_encode($kfmsg)]);
            // 发送认证通知
            try {
                if ($before_auth == 0 && $after_auth == 1) {
                    (new NoticeService())->authenticationSuccess($user->uid);
                }
            } catch (\Exception $e) {
                app('sentry')->captureException($e);
            }

            return array(
                "code" => 200,
                "message" => "验证成功",
            );
        } catch (\Exception $exception) {
            app('sentry')->captureException($exception);
            $kfmsg = array(
                'task_type' => 1,
                'task_dict' => array(
                    'wxid_to' => $wxid,
                    'at_list' => "",
                    'msg_list' => array(
                        [
                            'msg_type' => 1,
                            'msg' => "很抱歉，认证失败。\n\n可能原因：\n①您未设置微信号，请在微信客户端【我-微信号】设置。\n②小遇出错了，暂时没有能够检测到您设置的微信号。\n\n请确保您已设置微信号后，👇点击下方链接提醒小象导帮您人工审核",
                        ],
                        [
                            'msg_type' => 1,
                            'msg' => "点击链接提醒小遇人工审核：\nhttps://m.fenpeiduixiang.com/fpdx-wxauth/artifical.html?uid={$user->uid}&wxid={$wxid}&kf={$wxkf}",
                        ],
                    ),
                ),
            );
            Redis::sadd("wehub:task:list:{$wxkf}", [json_encode($kfmsg)]);

            return array(
                "code" => 101,
                "message" => "已提交验证,请耐心等待",
            );
        }
    }

    /**
     * 申请人工认证微信提醒
     * @param  array  $content
     * @param  string  $url
     * @throws \Exception
     */
    public function wxverifyWarn(array $content, string $url)
    {
        $admins = array(
            10002 => "oCyEB1S75zmwncQpLI8ljxq-4oPg",
            103143 => "oCyEB1ebnmeW_OUXZuaGq6-Hb-5Y",
        );

        $public_id = "gh_43ba50c16fba";
        $access_token = TokenModel::getToken($public_id);
        $apiurl = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token={$access_token}";
        foreach ($admins as $admin) {
            $data = [
                'touser' => $admin,
                'template_id' => 'DhPjGWXQZqKd4ic9hSTccOOSm3AGHiYjM1ppb7td8Wo',
                'url' => $url,
                'data' => $content,
            ];
            \Curl::to($apiurl)->withData($data)->asJson()->post();
        }
    }

    /**
     * 成为vip
     * @param  int  $uid
     * @param  bool  $bevip  是否成为vip
     * @param  bool  $isAllNotice
     * @return bool
     */
    public function beVip(int $uid, $bevip = true, $isAllNotice = false)
    {
        $ns = new \App\Services\User\NoticeService();
        /** @var UserModel $user */
        $user = UserModel::findOrFail($uid);
        $platform = \Config::get('platform');
        if ($user->be_vip_at > 0 && $bevip === true) {
            if ($isAllNotice) {
                $ns->bevip($user->uid);
            }

            return true;
        }
        if ($bevip) {
            $user->be_vip_at = time();
            VipLogModel::openVip($uid);
            try {
                $ns->bevip($user->uid);
                dispatch_now(new UserVipReportJob($user, true, $platform));
            } catch (\Exception $exception) {
            }
        } else {
            $auth = AuthKey::where([['auth_type', 'kdgx_unionid'], ['uid', $uid]])->first();
            if (
                $auth && !Openid::where([['unionid', $auth->auth_key], ['subscribe', 1]])->whereIn(
                    'public_id',
                    ['gh_c94c95866ca5', 'gh_b598cb7474d8']
                )->exists()
            ) {
                $user->be_vip_at = 0;
                VipLogModel::closeVip($uid);
                try {
                    $ns->outvip($user->uid);
                    dispatch_now(new UserVipReportJob($user, false, $platform));
                } catch (\Exception $exception) {
                    SentryFacade::captureMessage($exception);
                }
            } else {
                return false;
            }
        }
        $user->save();

        return false;
    }
}
