"""کلاینت Payamak — پورت مستقیم torbatkar-back/SMSPanel/utils.py (send_pattern_sms)."""
from __future__ import annotations

import asyncio
import json
import re
from typing import Any

import requests

from app.core.config import settings
from app.core.logging import get_logger

logger = get_logger(__name__)

# همان ثابت‌های torbatkar-back
PAYAMAK_TIMEOUT = (18, 45)
PAYAMAK_PATTERN_URL = "https://api.payamak-panel.com/post/send.asmx/SendByBaseNumber"
PAYAMAK_REST_PATTERN_URL = "https://rest.payamak-panel.com/api/SendSMS/BaseServiceNumber"


class SmsDeliveryError(Exception):
    def __init__(self, message: str) -> None:
        self.message = message
        super().__init__(message)


def payamak_is_configured() -> bool:
    return bool(settings.payamak_username and settings.payamak_auth_secret)


def _normalize_phone_for_sms(phone: str | None) -> str | None:
    """نرمال‌سازی شماره به فرمت 09xxxxxxxxx — همان torbatkar."""
    if phone is None:
        return None
    digits = re.sub(r"\D", "", str(phone))
    if digits.startswith("0098"):
        digits = digits[4:]
    elif digits.startswith("98") and len(digits) >= 12:
        digits = digits[2:]
    if len(digits) == 10:
        digits = "0" + digits
    if len(digits) == 11 and digits.startswith("0"):
        return digits
    return None


def _parse_payamak_response_body(raw_text: str) -> dict[str, Any]:
    if not raw_text or not str(raw_text).strip():
        return {}
    raw_text = str(raw_text).strip()
    try:
        data = json.loads(raw_text)
        if isinstance(data, dict) and isinstance(data.get("d"), str):
            try:
                inner = json.loads(data["d"])
                if isinstance(inner, dict):
                    return inner
            except json.JSONDecodeError:
                pass
        return data if isinstance(data, dict) else {"value": data}
    except json.JSONDecodeError:
        pass
    m = re.search(r"<string[^>]*>([\s\S]*?)</string>", raw_text, re.I)
    if m:
        inner = m.group(1).strip()
        try:
            parsed = json.loads(inner)
            return parsed if isinstance(parsed, dict) else {"value": parsed}
        except json.JSONDecodeError:
            pass
    m = re.search(r"<SendByBaseNumberResult[^>]*>([^<]+)</SendByBaseNumberResult>", raw_text, re.I)
    if m:
        value = m.group(1).strip()
        try:
            n = int(value)
            return {"RetStatus": n} if n > 0 else {"RetStatus": n, "raw": value}
        except ValueError:
            return {"raw": value}
    m = re.search(r'"RetStatus"\s*:\s*(-?\d+)', raw_text)
    if m:
        return {"RetStatus": int(m.group(1)), "_parse_fallback": True}
    return {"raw": raw_text[:800]}


def _payamak_pattern_logical_success(response_data: dict[str, Any]) -> tuple[bool, str | None]:
    if not isinstance(response_data, dict):
        return False, "پاسخ نامعتبر از پنل"
    ss = (response_data.get("StrRetStatus") or response_data.get("strRetStatus") or "").strip()
    if ss.lower() == "ok":
        return True, None
    rs = response_data.get("RetStatus")
    try:
        n = int(float(rs))
        if n > 0:
            return True, None
        return False, ss or f"خطای پنل پیامک (کد: {n})"
    except (TypeError, ValueError):
        pass
    raw = response_data.get("raw")
    if raw:
        return False, str(raw)[:300]
    return False, "نتوانستیم نتیجه ارسال را تشخیص دهیم"


def send_pattern_sms_sync(
    to: str,
    text: list[str] | str,
    body_id: int,
    *,
    username: str | None = None,
    password: str | None = None,
) -> dict[str, Any]:
    """ارسال پترن — کپی منطق torbatkar-back/SMSPanel/utils.send_pattern_sms."""
    username = username or settings.payamak_username
    password = password or settings.payamak_auth_secret
    to = _normalize_phone_for_sms(to) or str(to)

    if not all([username, password, to, text, body_id]):
        return {"success": False, "error": "پارامترهای لازم ارسال نشده است"}

    if not isinstance(text, list):
        text = [text]

    try:
        bid = int(body_id)
    except (TypeError, ValueError):
        return {"success": False, "error": "شناسه الگو (bodyId) نامعتبر است"}

    form_items = [
        ("username", str(username)),
        ("password", str(password)),
        ("to", str(to)),
        ("bodyId", str(bid)),
    ]
    for part in text:
        form_items.append(("text", str(part)))

    json_payload = {
        "username": username,
        "password": password,
        "to": str(to),
        "text": text,
        "bodyId": bid,
    }

    text_param = (
        json.dumps(text, ensure_ascii=False)
        if isinstance(text, list) and len(text) > 1
        else str(text[0] if isinstance(text, list) else text)
    )

    rest_form = {
        "username": str(username),
        "password": str(password),
        "to": str(to),
        "bodyId": str(bid),
        "text": text_param,
    }

    attempts = [
        ("rest_https", PAYAMAK_REST_PATTERN_URL, {"data": rest_form}),
        ("asmx_https", PAYAMAK_PATTERN_URL, {"data": form_items}),
    ]

    response = None
    response_data: dict[str, Any] = {}
    success = False
    err_logical: str | None = None
    last_transport_error: str | None = None

    for _label, url, req_kw in attempts:
        try:
            req_kw = {**req_kw, "timeout": PAYAMAK_TIMEOUT}
            response = requests.post(url, **req_kw)
            response_data = _parse_payamak_response_body(response.text)
            ok_http = response.status_code == 200
            ok_logical, err_logical = _payamak_pattern_logical_success(response_data)
            success = ok_http and ok_logical
            if success:
                break
            if not ok_logical and err_logical:
                last_transport_error = err_logical
        except requests.RequestException as exc:
            last_transport_error = str(exc)
            continue

    if not success:
        try:
            response = requests.post(PAYAMAK_PATTERN_URL, json=json_payload, timeout=PAYAMAK_TIMEOUT)
            response_data = _parse_payamak_response_body(response.text)
            ok_http = response.status_code == 200
            ok_logical, err_logical = _payamak_pattern_logical_success(response_data)
            success = ok_http and ok_logical
        except requests.RequestException as exc:
            last_transport_error = str(exc)

    err_msg = None if success else (err_logical or last_transport_error or "خطا در ارسال پیامک")

    if success:
        return {
            "success": True,
            "message_id": str(response_data.get("RetStatus", "")),
            "api_response": response_data,
        }
    return {
        "success": False,
        "error": err_msg,
        "api_response": response_data,
    }


class PayamakClient:
    async def send_otp(self, mobile: str, code: str) -> None:
        if not payamak_is_configured():
            if settings.environment in ("development", "test"):
                logger.info("payamak_otp_dev_mode", extra={"mobile": mobile, "code": code})
                return
            raise SmsDeliveryError("تنظیمات API پیامک در سرور پیکربندی نشده است")

        phone_for_sms = _normalize_phone_for_sms(mobile)
        if not phone_for_sms:
            raise SmsDeliveryError("شماره موبایل نامعتبر است")

        result = await asyncio.to_thread(
            send_pattern_sms_sync,
            phone_for_sms,
            [str(code)],
            settings.payamak_verification_body_id,
        )

        if not result.get("success"):
            err = result.get("error") or "ارسال پیامک ناموفق بود"
            if "name resolution" in str(err).lower() or "resolve" in str(err).lower():
                err = "سرویس پیامک موقتاً در دسترس نیست. لطفاً چند دقیقه بعد دوباره تلاش کنید."
            logger.error(
                "payamak_otp_failed",
                extra={"mobile": phone_for_sms, "error": err, "api": result.get("api_response")},
            )
            raise SmsDeliveryError(err)

        logger.info(
            "payamak_otp_sent",
            extra={"mobile": phone_for_sms, "message_id": result.get("message_id")},
        )
