"""
阿里云短信服务工具类
"""
import json
import random
import logging
from datetime import timedelta
from django.conf import settings
from django.utils import timezone

logger = logging.getLogger(__name__)

try:
    from alibabacloud_dysmsapi20170525.client import Client
    from alibabacloud_tea_openapi.models import Config
    from alibabacloud_tea_util.models import RuntimeOptions
    from alibabacloud_dysmsapi20170525.models import SendSmsRequest
    SMS_SDK_AVAILABLE = True
except ImportError:
    SMS_SDK_AVAILABLE = False


class SMSClient:
    """阿里云短信客户端"""

    _instance = None

    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
            cls._instance._initialized = False
        return cls._instance

    def __init__(self):
        if self._initialized:
            return

        if not SMS_SDK_AVAILABLE:
            self.client = None
            self._initialized = True
            return

        sms_config = settings.ALIYUN_SMS
        if not sms_config.get('ACCESS_KEY_ID'):
            self.client = None
            self._initialized = True
            return

        config = Config(
            access_key_id=sms_config['ACCESS_KEY_ID'],
            access_key_secret=sms_config['ACCESS_KEY_SECRET'],
            endpoint='dysmsapi.aliyuncs.com',
        )
        self.client = Client(config)
        self.sign_name = sms_config['SIGN_NAME']
        self.template_code = sms_config['TEMPLATE_CODE']
        self._initialized = True

    def send_code(self, phone, code):
        """
        发送验证码短信
        :param phone: 手机号
        :param code: 验证码
        :return: (success: bool, error_msg: str)
        """
        if not self.client:
            logger.warning('SMS SDK 未配置，验证码: %s -> %s', phone, code)
            return True, ''

        try:
            request = SendSmsRequest(
                phone_numbers=phone,
                sign_name=self.sign_name,
                template_code=self.template_code,
                template_param=json.dumps({'code': code}),
            )
            runtime = RuntimeOptions()
            response = self.client.send_sms_with_options(request, runtime)

            if response.body.code == 'OK':
                logger.info('短信发送成功: %s', phone)
                return True, ''
            else:
                msg = response.body.message or response.body.code
                logger.error('短信发送失败: %s, %s', phone, msg)
                return False, msg
        except Exception as e:
            logger.error('短信发送异常: %s, %s', phone, str(e))
            return False, str(e)


def get_sms_client():
    """获取短信客户端单例"""
    return SMSClient()


def generate_code():
    """生成随机验证码"""
    length = settings.ALIYUN_SMS.get('CODE_LENGTH', 6)
    return ''.join([str(random.randint(0, 9)) for _ in range(length)])


def send_sms_code(phone):
    """
    发送短信验证码的完整流程
    :param phone: 手机号
    :return: (success: bool, error_msg: str)
    """
    from apps.users.models import SmsCode

    sms_config = settings.ALIYUN_SMS
    interval = sms_config.get('SEND_INTERVAL', 60)
    expire_seconds = sms_config.get('CODE_EXPIRE', 300)

    # 检查发送频率
    recent = SmsCode.objects.filter(
        phone=phone,
        created_at__gte=timezone.now() - timedelta(seconds=interval),
    ).exists()
    if recent:
        return False, '验证码发送过于频繁，请稍后再试'

    # 生成验证码
    code = generate_code()

    # 发送短信
    client = get_sms_client()
    ok, err = client.send_code(phone, code)
    if not ok:
        return False, err or '短信发送失败'

    # 保存到数据库
    SmsCode.objects.create(
        phone=phone,
        code=code,
        expire_at=timezone.now() + timedelta(seconds=expire_seconds),
    )

    return True, ''


def verify_sms_code(phone, code):
    """
    校验短信验证码
    :param phone: 手机号
    :param code: 验证码
    :return: (valid: bool, error_msg: str)
    """
    # DEBUG 模式下，万能验证码跳过校验
    if settings.DEBUG and code == '999999':
        return True, ''

    from apps.users.models import SmsCode

    record = SmsCode.objects.filter(
        phone=phone,
        code=code,
        is_used=False,
        expire_at__gt=timezone.now(),
    ).order_by('-created_at').first()

    if not record:
        return False, '验证码无效或已过期'

    # 标记已使用
    record.is_used = True
    record.save(update_fields=['is_used'])

    return True, ''
