<?php
declare (strict_types=1);

namespace app\common\service\sms;

use Overtrue\EasySms\EasySms;
use app\common\model\sms\SmsTemplate;
use app\common\model\sms\SmsConfig;
use app\common\model\sms\SmsLog;
use app\common\model\sms\SmsCode;
use core\base\BaseService;

class SmsService extends BaseService
{
    private EasySms $easySms;

    public const CODE_EXPIRE_TIME = 300;
    public const SEND_FREQUENCY_LIMIT = 60;

    public function __construct()
    {
        parent::__construct();
        $this->initEasySms();
    }

    public function sendByTemplate(string $mobile, string $templateKey, array $params = []): bool
    {
        $templateValue = '';
        try {
            $template = SmsTemplate::where('template_key', $templateKey)->find();

            if (!$template) {
                self::setError("短信模板不存在: {$templateKey}");
                return false;
            }

            $templateValue = $template['template_value'];
            $message = [
                'template' => $templateValue,
                'data' => $params,
            ];

            $result = $this->easySms->send($mobile, $message);
            $this->logSmsResult($mobile, $templateKey, $templateValue, $params, $result, true);

            return true;
        } catch (\Throwable $e) {
            $errorMessage = $e->getMessage();
            if (method_exists($e, 'getExceptions')) {
                $exceptions = $e->getExceptions();
                if (!empty($exceptions)) {
                    $messages = [];
                    foreach ($exceptions as $gateway => $ex) {
                        $messages[] = $gateway . ': ' . $ex->getMessage();
                    }
                    $errorMessage = implode(' | ', $messages);
                }
            }
            self::setError($errorMessage);
            $this->logSmsResult($mobile, $templateKey, $templateValue, $params, [], false, $e->getMessage());
            return false;
        }
    }

    public function sendVerifyCode(string $mobile, string $code, string $templateKey = 'verify_code'): bool
    {
        if (!$this->checkSendFrequency($mobile, $templateKey)) {
            self::setError('发送过于频繁，请稍后再试');
            return false;
        }

        $params = ['code' => $code];
        $result = $this->sendByTemplate($mobile, $templateKey, $params);

        if ($result) {
            SmsCode::where('mobile', $mobile)
                ->where('template_key', $templateKey)
                ->where('status', 0)
                ->update(['status' => 2, 'update_time' => time()]);

            SmsCode::create([
                'mobile' => $mobile,
                'code' => $code,
                'template_key' => $templateKey,
                'status' => 0,
                'expire_time' => time() + self::CODE_EXPIRE_TIME,
                'create_time' => time(),
                'update_time' => time(),
            ]);
        }

        return $result;
    }

    public function verifyCode(string $mobile, string $code, string $templateKey = 'verify_code'): bool
    {
        $smsCode = SmsCode::where('mobile', $mobile)
            ->where('template_key', $templateKey)
            ->where('status', 0)
            ->where('expire_time', '>', time())
            ->order('id', 'desc')
            ->find();

        if (!$smsCode) {
            self::setError('验证码已过期或不存在');
            return false;
        }

        if ($smsCode['code'] !== $code) {
            self::setError('验证码错误');
            return false;
        }

        $smsCode->save([
            'status' => 1,
            'update_time' => time(),
        ]);

        return true;
    }

    private function initEasySms(): void
    {
        $easySmsConfig = EasySmsConfig::generateConfig();
        $this->easySms = new EasySms($easySmsConfig);
    }

    private function checkSendFrequency(string $mobile, string $templateKey): bool
    {
        $latest = SmsCode::where('mobile', $mobile)
            ->where('template_key', $templateKey)
            ->order('id', 'desc')
            ->find();

        if (!$latest) {
            return true;
        }

        return (time() - (int)$latest['create_time']) >= self::SEND_FREQUENCY_LIMIT;
    }

    private function getActiveGateway(): string
    {
        return (string)SmsConfig::where('status', 1)->value('gateway');
    }

    private function logSmsResult(
        string $mobile,
        string $templateKey,
        string $templateValue,
        array $params,
        array $result,
        bool $success,
        string $errorMessage = ''
    ): void {
        $gateway = $this->getActiveGateway();
        $status = $success ? 1 : 2;
        $requestId = null;
        $bizId = null;
        $errorCode = null;

        $gatewayResult = [];
        if (!empty($result)) {
            $gatewayResult = $result[$gateway] ?? reset($result) ?: [];
        }

        if (is_array($gatewayResult)) {
            $response = $gatewayResult['result'] ?? [];
            $requestId = $response['RequestId'] ?? $response['request_id'] ?? null;
            $bizId = $response['BizId'] ?? $response['biz_id'] ?? null;
            $errorCode = $response['Code'] ?? $response['code'] ?? null;

            if (($gatewayResult['status'] ?? '') === 'failure' && empty($errorMessage)) {
                $errorMessage = $gatewayResult['message'] ?? $errorMessage;
            }
        }

        SmsLog::create([
            'mobile' => $mobile,
            'template_key' => $templateKey,
            'template_value' => $templateValue,
            'params' => $params,
            'content' => '',
            'gateway' => $gateway,
            'request_id' => $requestId,
            'biz_id' => $bizId,
            'status' => $status,
            'error_code' => $errorCode,
            'error_message' => $errorMessage,
            'send_time' => time(),
            'create_time' => time(),
        ]);
    }
}
