"""短信验证码服务 —— provider 分派入口。

对外只暴露 `send_code` / `verify_code` / `SmsError`,api 层无需关心用哪个 provider。
provider 由 `settings.SMS_PROVIDER` 选择(**每次调用读取**,支持运行时切换 + 灰度回退):
  - `jiguang`(默认):自管码(本服务生成、内存存/校验,极光只发)。见 [jiguang.py](jiguang.py)。
  - `aliyun`:阿里云号码认证(阿里云生成+下发+校验,Mode A)。见 [aliyun.py](aliyun.py)。
  - `chuanglan`:创蓝云智(253)模板短信,自管码 Mode B(本服务生成、内存存/校验,创蓝只发)。见 [chuanglan.py](chuanglan.py)。

mock(`SMS_MOCK=true`)与各 provider 的行为差异都封在 provider 内部;本层只做路由。
拆包前本模块是单文件 `sms.py`;拆包后极光逻辑迁入 `jiguang` 子模块,行为零改动。
"""
from __future__ import annotations

from app.core.config import settings

from . import aliyun, chuanglan, jiguang
from .base import SmsError

__all__ = ["SmsError", "send_code", "verify_code"]

# provider 名 -> 模块;未知/缺省值回退 jiguang(默认兜底,防误配把登录打挂)。
_PROVIDERS = {"aliyun": aliyun, "chuanglan": chuanglan}


def _provider():
    """按配置选 provider 模块(每次调用读 settings,支持运行时切换 / 测试注入)。"""
    return _PROVIDERS.get(settings.SMS_PROVIDER, jiguang)


def send_code(phone: str) -> int:
    """发送验证码,返回距下次可发的冷却秒数;失败抛 SmsError。委托给当前 provider。"""
    return _provider().send_code(phone)


def verify_code(phone: str, code: str) -> bool:
    """校验验证码,返回是否通过;provider 异常降级抛 SmsError。委托给当前 provider。"""
    return _provider().verify_code(phone, code)
