from __future__ import annotations

import json
from typing import Any

import aiohttp

from mode.verification.base import RedirectSession, VerificationProviderError, VerificationResult

ALIYUN_IDCERT_ENDPOINT = "https://idcert.market.alicloudapi.com/idcard"
PASS_STATUS = "01"
REJECT_STATUS_MESSAGES = {
    "02": "实名认证不通过",
    "202": "无法认证，可能为库无或户籍状态未同步",
    "204": "姓名格式不正确",
    "205": "身份证格式不正确",
}
GATEWAY_ERROR_MESSAGES = {
    "Invalid Param Location": "参数错误",
    "Invalid AppCode": "AppCode 错误",
    "Invalid Url": "请求 Method、Path 或环境错误",
    "Unauthorized": "服务未被授权，或 URL/Path 不正确",
    "Quota Exhausted": "套餐包次数用完",
    "Api Market Subscription quota exhausted": "套餐包次数用完，请续购套餐",
}


def _clean_text(value: Any) -> str:
    return str(value or "").strip()


def _first_config_value(config: Any, *names: str) -> str:
    for name in names:
        value = _clean_text(getattr(config, name, None))
        if value:
            return value
    return ""


class AliyunIdcertProvider:
    name = "aliyun_idcert"
    supported_modes = ("two_factor",)

    def __init__(self, config: Any):
        self.appcode = _first_config_value(
            config,
            "ALIYUN_IDCERT_APPCODE",
            "ALIYUN_IDCERT_APP_CODE",
            "ALIYUN_APPCODE",
            "APP_CODE",
            "APPCODE",
        )
        self.endpoint = _first_config_value(config, "ALIYUN_IDCERT_ENDPOINT", "ENDPOINT") or ALIYUN_IDCERT_ENDPOINT
        self.timeout = aiohttp.ClientTimeout(total=float(getattr(config, "ALIYUN_IDCERT_TIMEOUT", 20) or 20))

        if not self.appcode:
            raise VerificationProviderError("阿里云市场二要素插件未配置 AppCode", status_code=400)

    def _gateway_error(self, *, http_status: int, reason: str, raw: Any) -> VerificationProviderError:
        message = GATEWAY_ERROR_MESSAGES.get(reason) or reason or "阿里云市场实名认证接口请求失败"
        if http_status == 403 and "quota" in message.lower():
            status_code = 429
        elif http_status == 400:
            status_code = 400
        else:
            status_code = 502
        return VerificationProviderError(f"阿里云市场二要素接口错误：{message}", status_code=status_code, raw=raw)

    async def verify_two_factor(self, *, real_name: str, id_card_no: str) -> VerificationResult:
        headers = {
            "Authorization": f"APPCODE {self.appcode}",
            "Accept": "application/json",
        }
        params = {
            "name": real_name,
            "idCard": id_card_no,
        }
        try:
            async with aiohttp.ClientSession(timeout=self.timeout) as session:
                async with session.get(self.endpoint, params=params, headers=headers) as response:
                    text = await response.text()
                    try:
                        body = json.loads(text) if text else {}
                    except json.JSONDecodeError as exc:
                        if response.status != 200:
                            reason = _clean_text(response.headers.get("X-Ca-Error-Message"))
                            raise self._gateway_error(http_status=response.status, reason=reason, raw=text) from exc
                        raise VerificationProviderError("阿里云市场二要素接口返回非 JSON 响应", status_code=502) from exc
                    if response.status != 200:
                        reason = _clean_text(response.headers.get("X-Ca-Error-Message"))
                        raise self._gateway_error(http_status=response.status, reason=reason, raw=body)
        except VerificationProviderError:
            raise
        except aiohttp.ClientError as exc:
            raise VerificationProviderError(f"请求阿里云市场二要素接口失败：{exc}", status_code=502) from exc

        status = _clean_text(body.get("status"))
        message = _clean_text(body.get("msg")) or REJECT_STATUS_MESSAGES.get(status) or "实名认证未通过"
        trace_id = _clean_text(body.get("traceId")) or None

        if status == PASS_STATUS:
            return VerificationResult(
                passed=True,
                mode="two_factor",
                provider=self.name,
                status="verified",
                reason="",
                external_id=trace_id,
                raw=body,
            )

        if status == "9999":
            raise VerificationProviderError("阿里云市场二要素接口系统维护", status_code=502, raw=body)

        return VerificationResult(
            passed=False,
            mode="two_factor",
            provider=self.name,
            status="rejected",
            reason=message,
            external_id=trace_id,
            raw=body,
        )

    async def verify_three_factor(
        self,
        *,
        real_name: str,
        id_card_no: str,
        phone: str,
    ) -> VerificationResult:
        raise VerificationProviderError("阿里云市场二要素插件不支持三要素核验", status_code=400)

    async def verify_face_api(
        self,
        *,
        real_name: str,
        id_card_no: str,
        image_base64: str,
        image_type: str = "BASE64",
    ) -> VerificationResult:
        raise VerificationProviderError("本地拍摄/上传扫脸已关闭，请使用第三方 H5 跳转核身", status_code=400)

    async def create_redirect_session(
        self,
        *,
        real_name: str,
        id_card_no: str,
        return_url: str,
        notify_url: str | None = None,
    ) -> RedirectSession:
        raise VerificationProviderError("阿里云市场二要素插件不支持 H5 跳转核身", status_code=400)


def create_provider(config: Any) -> AliyunIdcertProvider:
    return AliyunIdcertProvider(config)
