<?php

namespace App\Services;

use App\Exceptions\SmsException;
use App\Models\SmsRecord;
use Illuminate\Support\Facades\Redis;
use Illuminate\Support\Str;
use Overtrue\EasySms\EasySms;
use Overtrue\EasySms\Exceptions\Exception;
use Overtrue\EasySms\Exceptions\NoGatewayAvailableException;

class SmsService
{
    const EVENT_LOGIN = 100;

    const DEFAULT_SMS_CODE_LENGTH = 4;

    const DEFAULT_SMS_CODE_EXP_TIME = 120;

    const EVENTS_TO_TEMPLATE = [
        //'login' => 377532,   //好邻盟短信验证码为：{1}，请在{2}分钟内输入，如非本人操作，请忽略本短信
        'login' => "SMS_187241612",
//        'forget' => 377532,   //好邻盟短信验证码为：{1}，请在{2}分钟内输入，如非本人操作，请忽略本短信
        'forget' => "SMS_187241612",
    ];

    const EVENT_ITEM_FUNCTION = [

    ];

    /**
     * 获取easySms配置
     * @return \Illuminate\Config\Repository|\Illuminate\Contracts\Foundation\Application|mixed
     */
    static public function getEasySmsConfig()
    {
        return config('easysms');
    }

    /**
     * 获取 easySms 实例
     * @return EasySms
     */
    static public function easySms()
    {
        return new EasySms(self::getEasySmsConfig());
    }

    static public function send(int $mobile, string $event, array $eventData, $user = null)
    {
        try {
            self::verifySmsEvent($event);

            $templateId = self::getTemplateIdByEvent($event);

            $smsCode = self::getSmsCode();
            $result = self::easySms()->send($mobile, [
                'template' => $templateId,
                'content' => 'null',
                'data' => [
                    'code' => $smsCode,
//                    'expTime' => 10,
                ]
            ],['aliyun']);
            //$result = true;

            //缓存验证码
            $verifyKey = self::getVerifyKey();
            self::cacheSmsCodeByVerifyKey($mobile, $smsCode, $verifyKey);

            //存入发送记录
            self::storeSmsRecord($mobile, $event, $smsCode, $verifyKey, $result);

            return [
                'verifyKey' => $verifyKey,
                'code' => $smsCode,
            ];

        } catch (SmsException $e) {
            return ['error' => $e->getMessage()];
        } catch (NoGatewayAvailableException $e) {
//            return ['error' => json_encode($e)];
            return ['error' => '发送短信失败'];
        } catch (Exception $e) {
            return ['error' => '发送短信失败'];
        }
    }

    /**
     * 生成随机字符串用户校验验证码
     * @param int $length
     * @return string
     */
    static public function getVerifyKey(int $length = 10)
    {
        return Str::random($length);
    }

    /**
     * 生成短信验证码
     * @param int $length
     * @return int
     */
    static public function getSmsCode(int $length = self::DEFAULT_SMS_CODE_LENGTH)
    {
        return rand(1000, 9999);
    }

    /**
     * 根据验证码，verifyKey 校验是否正确
     * @param string $verifyKey
     * @param string $smsCode
     * @return bool
     * @throws SmsException
     */
    static public function checkSmsCodeByVerifyKey(string $verifyKey, string $smsCode)
    {
        if (!RedisService::redis()->exists('smsCode:verifyCode' . $verifyKey)) {
            throw new SmsException('验证码已过期');
        }
        $record = RedisService::redis()->get('smsCode:verifyCode' . $verifyKey);
        if (!json_decode($record, true)) {
            throw new SmsException('验证码解析失败');
        }
        $record = json_decode($record, true);
        if ($smsCode != $record['smsCode']) {
            throw new SmsException('验证码错误');
        }
        RedisService::redis()->del('smsCode:verifyCode' . $verifyKey);

        return true;
    }


    /**
     * 缓存短信验证码
     * @param int $mobile
     * @param string $smsCode
     * @param string $verifyKey
     * @param int $expTime
     * @return mixed
     */
    static public function cacheSmsCodeByVerifyKey(int $mobile, string $smsCode, string $verifyKey, int $expTime = self::DEFAULT_SMS_CODE_EXP_TIME)
    {
        return RedisService::redis()->SETEX('smsCode:verifyCode' . $verifyKey, $expTime, json_encode(['mobile' => $mobile, 'smsCode' => $smsCode]));
    }

    /**
     * 根据事件获取 短信模板ID
     * @param string $event
     * @return string
     * @throws SmsException
     */
    static public function getTemplateIdByEvent(string $event)
    {
        self::verifySmsEvent($event);
        return self::EVENTS_TO_TEMPLATE[$event];
    }

    /**
     * 验证发送事件
     * @param string $event
     * @return bool
     * @throws SmsException
     */
    static public function verifySmsEvent(string $event)
    {
        if (!array_key_exists($event, self::EVENTS_TO_TEMPLATE)) {
            throw new SmsException('事件类型错误');
        }
        return true;
    }

    /**
     * 将发送记录存到数据库以便调试
     * @param int $mobile
     * @param string $event
     * @param string $smsCode
     * @param string $verifyKey
     * @param  $sendResult
     * @return mixed
     */
    static public function storeSmsRecord(int $mobile, string $event, string $smsCode, string $verifyKey, $sendResult)
    {
        return SmsRecord::create([
            'mobile' => $mobile,
            'event' => $event,
            'sms_code' => $smsCode,
            'verify_key' => $verifyKey,
            'sms_result' => $sendResult
        ]);
    }
}
