# ruff: noqa: F401,F403,F405,I001
from ._runtime import *  # noqa: F403,F405

from bot.app.web.webapp.cache_helpers import invalidate_webapp_user_caches


def _billing_iso_datetime(value: Optional[Any]) -> Optional[str]:
    if not value:
        return None
    if isinstance(value, datetime):
        normalized = value if value.tzinfo else value.replace(tzinfo=timezone.utc)
        return normalized.isoformat()
    return str(value)


def _billing_datetime_text(value: Optional[Any]) -> Optional[str]:
    if not value:
        return None
    if isinstance(value, datetime):
        normalized = value if value.tzinfo else value.replace(tzinfo=timezone.utc)
        return normalized.strftime("%d.%m.%Y %H:%M")
    text = str(value)
    try:
        normalized = datetime.fromisoformat(text.replace("Z", "+00:00"))
        return normalized.strftime("%d.%m.%Y %H:%M")
    except Exception:
        return text


def _parse_positive_int_units(value: Any) -> Optional[int]:
    if isinstance(value, bool):
        return None
    try:
        number = float(value)
    except (TypeError, ValueError):
        return None
    if not number.is_integer():
        return None
    integer = int(number)
    return integer if integer > 0 else None


async def apply_promo_route(request: web.Request) -> web.Response:
    user_id = _require_user_id(request)
    payload = await _read_json(request)
    code = str(payload.get("code") or "").strip()
    if not code:
        return _json_error(400, "empty_code", "Promo code is empty")

    settings: Settings = request.app["settings"]
    promo_code_service: PromoCodeService = request.app.get("promo_code_service")
    if not promo_code_service:
        return _json_error(503, "service_unavailable", "Promo service unavailable")

    async_session_factory: sessionmaker = request.app["async_session_factory"]
    async with async_session_factory() as session:
        try:
            db_user = await user_dal.get_user_by_id(session, user_id)
            if not db_user or db_user.is_banned:
                await session.rollback()
                return _json_error(403, "access_denied", "Access denied")
            lang = _normalize_language(db_user.language_code or settings.DEFAULT_LANGUAGE)
            success, result = await promo_code_service.apply_promo_code(
                session,
                user_id,
                code,
                lang,
            )
            if not success:
                await session.commit()
                return _json_error(400, "promo_apply_failed", str(result))
            await session.commit()
            end_date = result if isinstance(result, datetime) else None
            return web.json_response(
                {
                    "ok": True,
                    "end_date": end_date.isoformat() if end_date else None,
                    "end_date_text": end_date.strftime("%d.%m.%Y %H:%M") if end_date else None,
                }
            )
        except Exception:
            await session.rollback()
            logger.exception("WebApp promo apply failed")
            return _json_error(500, "promo_apply_failed", "Promo apply failed")


async def create_payment_route(request: web.Request) -> web.Response:
    user_id = _require_user_id(request)
    rate_limit_response = await _enforce_webapp_rate_limit(
        request,
        user_id=user_id,
        action="payments_create",
    )
    if rate_limit_response:
        return rate_limit_response

    payload = await _read_json(request)
    payment_payload, validation_error = _validate_model_payload(WebAppPaymentCreatePayload, payload)
    if validation_error:
        return validation_error
    method = str(payment_payload.method or "").strip().lower()
    settings: Settings = request.app["settings"]
    subscription_service: SubscriptionService = request.app["subscription_service"]
    cached = _get_cached_webapp_settings(request)
    tariffs_config = settings.tariffs_config
    default_currency = default_currency_key_for_settings(settings)
    default_currency_code = payment_currency_code(default_currency)
    traffic_mode = bool(settings.traffic_sale_mode)
    sale_mode = "subscription"
    traffic_gb_for_payment: Optional[float] = None
    hwid_quote: Optional[Dict[str, Any]] = None
    requested_sale_mode = _sale_mode_base(str(payment_payload.sale_mode or ""))

    if tariffs_config and requested_sale_mode in {
        "hwid_device",
        "hwid_devices",
        "hwid_devices_renewal",
    }:
        tariff_key = str(payment_payload.tariff_key or "").strip()
        if not tariff_key:
            return _json_error(400, "invalid_plan", "Tariff is not selected")
        try:
            tariff = tariffs_config.require(tariff_key)
        except Exception:
            return _json_error(400, "invalid_plan", "Tariff is not available")
        if tariff.billing_model != "period":
            return _json_error(400, "invalid_plan", "Device top-up is not available")
        device_count = _parse_positive_int_units(
            payment_payload.device_count
            if payment_payload.device_count is not None
            else payment_payload.months
        )
        if device_count is None:
            return _json_error(400, "invalid_plan", "Invalid device package")
        if not tariff.hwid_device_packages:
            return _json_error(400, "invalid_plan", "Device package is not available")
        payment_units = device_count
        sale_mode = f"{requested_sale_mode}@{tariff.key}"
    elif tariffs_config and requested_sale_mode in {"topup", "premium_topup"}:
        tariff_key = str(payment_payload.tariff_key or "").strip()
        if not tariff_key:
            return _json_error(400, "invalid_plan", "Tariff is not selected")
        try:
            tariff = tariffs_config.require(tariff_key)
        except Exception:
            return _json_error(400, "invalid_plan", "Tariff is not available")
        try:
            traffic_gb = float(
                payment_payload.traffic_gb
                if payment_payload.traffic_gb is not None
                else payment_payload.months
            )
        except (TypeError, ValueError):
            return _json_error(400, "invalid_plan", "Invalid traffic package")
        packages = (
            tariff.premium_topup_packages
            if requested_sale_mode == "premium_topup"
            else tariffs_config.topup_packages_for(tariff)
        )
        currency_packages = {
            float(package.gb): float(package.price)
            for package in (packages.for_currency(default_currency) if packages else [])
        }
        stars_packages = {
            float(package.gb): int(float(package.price))
            for package in (packages.stars if packages else [])
        }
        package_key = _resolve_numeric_option_key(currency_packages, traffic_gb)
        stars_package_key = _resolve_numeric_option_key(stars_packages, traffic_gb)
        price = currency_packages.get(package_key) if package_key is not None else None
        stars_price = (
            stars_packages.get(stars_package_key) if stars_package_key is not None else None
        )
        if price is None and method != "stars":
            return _json_error(400, "invalid_plan", "Traffic package is not available")
        if method == "stars" and (stars_price is None or int(stars_price) <= 0):
            return _json_error(400, "invalid_plan", "Stars price is not configured")
        payment_units = int(traffic_gb) if float(traffic_gb).is_integer() else traffic_gb
        traffic_gb_for_payment = float(payment_units)
        sale_mode = f"{requested_sale_mode}@{tariff.key}"
    elif tariffs_config:
        tariff_key = str(payment_payload.tariff_key or "").strip()
        if not tariff_key:
            return _json_error(400, "invalid_plan", "Tariff is not selected")
        try:
            tariff = tariffs_config.require(tariff_key)
        except Exception:
            return _json_error(400, "invalid_plan", "Tariff is not available")

        if tariff.billing_model == "traffic":
            try:
                traffic_gb = float(
                    payment_payload.traffic_gb
                    if payment_payload.traffic_gb is not None
                    else payment_payload.months
                )
            except (TypeError, ValueError):
                return _json_error(400, "invalid_plan", "Invalid traffic package")
            if traffic_gb <= 0:
                return _json_error(400, "invalid_plan", "Invalid traffic package")
            currency_packages = {
                float(package.gb): float(package.price)
                for package in (
                    tariff.traffic_packages.for_currency(default_currency)
                    if tariff.traffic_packages
                    else []
                )
            }
            stars_packages = {
                float(package.gb): int(float(package.price))
                for package in (tariff.traffic_packages.stars if tariff.traffic_packages else [])
            }
            package_key = _resolve_numeric_option_key(currency_packages, traffic_gb)
            stars_package_key = _resolve_numeric_option_key(stars_packages, traffic_gb)
            price = currency_packages.get(package_key) if package_key is not None else None
            stars_price = (
                stars_packages.get(stars_package_key) if stars_package_key is not None else None
            )
            if price is None and method != "stars":
                return _json_error(400, "invalid_plan", "Traffic package is not available")
            if method == "stars" and (stars_price is None or int(stars_price) <= 0):
                return _json_error(400, "invalid_plan", "Stars price is not configured")
            payment_units = int(traffic_gb) if float(traffic_gb).is_integer() else traffic_gb
            traffic_gb_for_payment = float(payment_units)
            sale_mode = f"traffic_package@{tariff.key}"
        else:
            try:
                months = int(float(payment_payload.months))
            except (TypeError, ValueError):
                return _json_error(400, "invalid_plan", "Invalid subscription period")
            if months not in tariff.enabled_periods:
                return _json_error(400, "invalid_plan", "Subscription period is not available")
            price = tariff.period_price(months, default_currency)
            stars_price_raw = tariff.period_price(months, "stars")
            stars_price = int(stars_price_raw) if stars_price_raw and stars_price_raw > 0 else None
            if price is None and method != "stars":
                return _json_error(400, "invalid_plan", "Subscription period is not available")
            if method == "stars" and (stars_price is None or int(stars_price) <= 0):
                return _json_error(400, "invalid_plan", "Stars price is not configured")
            payment_units = months
            sale_mode = f"subscription@{tariff.key}"
    elif traffic_mode:
        try:
            traffic_gb = float(
                payment_payload.traffic_gb
                if payment_payload.traffic_gb is not None
                else payment_payload.months
            )
        except (TypeError, ValueError):
            return _json_error(400, "invalid_plan", "Invalid traffic package")
        if traffic_gb <= 0:
            return _json_error(400, "invalid_plan", "Invalid traffic package")
        package_key = _resolve_numeric_option_key(cached["traffic_packages"], traffic_gb)
        stars_package_key = _resolve_numeric_option_key(
            cached["stars_traffic_packages"], traffic_gb
        )
        price = cached["traffic_packages"].get(package_key) if package_key is not None else None
        stars_price = (
            cached["stars_traffic_packages"].get(stars_package_key)
            if stars_package_key is not None
            else None
        )
        if price is None and method != "stars":
            return _json_error(400, "invalid_plan", "Traffic package is not available")
        if method == "stars" and (stars_price is None or int(stars_price) <= 0):
            return _json_error(400, "invalid_plan", "Stars price is not configured")
        payment_units = int(traffic_gb) if float(traffic_gb).is_integer() else traffic_gb
        traffic_gb_for_payment = float(payment_units)
        sale_mode = "traffic"
    else:
        try:
            months = int(float(payment_payload.months))
        except (TypeError, ValueError):
            return _json_error(400, "invalid_plan", "Invalid subscription period")
        price = cached["subscription_options"].get(months)
        stars_price = cached["stars_subscription_options"].get(months)
        if price is None and method != "stars":
            return _json_error(400, "invalid_plan", "Subscription period is not available")
        if method == "stars" and (stars_price is None or int(stars_price) <= 0):
            return _json_error(400, "invalid_plan", "Stars price is not configured")
        payment_units = months
        sale_mode = "subscription"

    async_session_factory: sessionmaker = request.app["async_session_factory"]
    async with async_session_factory() as session:
        db_user = await user_dal.get_user_by_id(session, user_id)
        if not db_user or db_user.is_banned:
            return _json_error(403, "access_denied", "Access denied")
        lang = db_user.language_code or settings.DEFAULT_LANGUAGE
        if _sale_mode_is_hwid_devices(sale_mode):
            sub = await subscription_dal.get_active_subscription_by_user_id(
                session, user_id, db_user.panel_user_uuid
            )
            sale_tariff_key = _sale_mode_tariff_key(sale_mode)
            if not sub or not sub.tariff_key or sub.tariff_key != sale_tariff_key:
                return _json_error(
                    400, "subscription_required", "Active tariff subscription is required"
                )
            try:
                active_tariff = tariffs_config.require(sub.tariff_key) if tariffs_config else None
            except Exception:
                active_tariff = None
            if not active_tariff or active_tariff.billing_model != "period":
                return _json_error(400, "invalid_plan", "Device top-up is not available")
            currency = "stars" if method == "stars" else default_currency
            hwid_quote = await subscription_service.quote_hwid_device_topup(
                session,
                user_id=user_id,
                device_count=int(payment_units),
                tariff_key=sale_tariff_key,
                renewal=_sale_mode_base(sale_mode) == "hwid_devices_renewal",
                currency=currency,
            )
            if not hwid_quote:
                return _json_error(400, "invalid_plan", "Device package is not available")
            if method == "stars":
                stars_price = int(hwid_quote["price"])
                price = 0.0
                if stars_price <= 0:
                    return _json_error(400, "invalid_plan", "Stars price is not configured")
            else:
                price = float(hwid_quote["price"])
                stars_price = None
        admin_ids = {int(item) for item in (settings.ADMIN_IDS or [])}
        is_admin = bool(db_user.telegram_id and int(db_user.telegram_id) in admin_ids)
        return await _create_subscription_payment(
            request=request,
            session=session,
            user_id=user_id,
            method=method,
            months=payment_units,
            price=float(price or 0),
            stars_price=stars_price,
            currency=default_currency_code,
            lang=lang,
            sale_mode=sale_mode,
            traffic_gb=traffic_gb_for_payment,
            is_admin=is_admin,
            hwid_quote=hwid_quote,
        )


async def activate_trial_route(request: web.Request) -> web.Response:
    user_id = _require_user_id(request)
    rate_limit_response = await _enforce_webapp_rate_limit(
        request,
        user_id=user_id,
        action="trial_activate",
    )
    if rate_limit_response:
        return rate_limit_response

    settings: Settings = request.app["settings"]
    if not settings.TRIAL_ENABLED or settings.TRIAL_DURATION_DAYS <= 0:
        return _json_error(400, "trial_unavailable", "Trial is not available")

    async_session_factory: sessionmaker = request.app["async_session_factory"]
    subscription_service: SubscriptionService = request.app["subscription_service"]
    async with async_session_factory() as session:
        db_user = await user_dal.get_user_by_id(session, user_id)
        if not db_user or db_user.is_banned:
            return _json_error(403, "access_denied", "Access denied")

        activation_result = await subscription_service.activate_trial_subscription(session, user_id)
        if not activation_result or not activation_result.get("activated"):
            await session.rollback()
            message_key = (
                activation_result.get("message_key", "trial_activation_failed")
                if activation_result
                else "trial_activation_failed"
            )
            status = 400 if message_key != "trial_activation_failed_panel_update" else 502
            return _json_error(status, message_key, message_key)

        end_date = activation_result.get("end_date")
        config_link, connect_url = await prepare_config_links(
            settings,
            activation_result.get("subscription_url"),
        )

        i18n_instance = request.app.get("i18n")
        if settings.LOG_TRIAL_ACTIVATIONS and i18n_instance:
            try:
                from bot.services.notification_service import NotificationService

                notification_service = NotificationService(
                    request.app["bot"], settings, i18n_instance
                )
                await notification_service.notify_trial_activation(
                    user_id,
                    end_date,
                    username=db_user.username,
                    email=getattr(db_user, "email", None),
                )
            except Exception:
                logger.exception("Failed to send WebApp trial activation notification")

        try:
            from db.dal import ad_dal as _ad_dal

            await _ad_dal.mark_trial_activated(session, user_id)
            await session.commit()
        except Exception:
            await session.rollback()
            logger.exception("Failed to mark WebApp trial activation for ad attribution")

        await invalidate_webapp_user_caches(settings, user_id)

        return web.json_response(
            {
                "ok": True,
                "activated": True,
                "days": activation_result.get("days", settings.TRIAL_DURATION_DAYS),
                "end_date": end_date.isoformat() if isinstance(end_date, datetime) else None,
                "end_date_text": _format_webapp_datetime(end_date)
                if isinstance(end_date, datetime)
                else None,
                "traffic_gb": activation_result.get("traffic_gb", settings.TRIAL_TRAFFIC_LIMIT_GB),
                "config_link": config_link,
                "connect_url": connect_url or config_link,
            }
        )


async def tariff_topup_options_route(request: web.Request) -> web.Response:
    user_id = _require_user_id(request)
    settings: Settings = request.app["settings"]
    config = settings.tariffs_config
    topup_kind = str(request.query.get("kind") or "all").strip().lower()
    if topup_kind not in {"all", "regular", "premium"}:
        return _json_error(400, "invalid_topup_kind", "Invalid topup kind")
    if not config:
        return _json_error(404, "tariffs_unavailable", "Tariffs are not configured")

    async_session_factory: sessionmaker = request.app["async_session_factory"]
    async with async_session_factory() as session:
        db_user = await user_dal.get_user_by_id(session, user_id)
        if not db_user or db_user.is_banned:
            return _json_error(403, "access_denied", "Access denied")
        sub = await subscription_dal.get_active_subscription_by_user_id(
            session, user_id, db_user.panel_user_uuid
        )
        if not sub or not sub.tariff_key:
            return _json_error(
                400, "subscription_required", "Active tariff subscription is required"
            )
        lang = db_user.language_code or settings.DEFAULT_LANGUAGE
        tariff = config.require(sub.tariff_key)
        plans = (
            _serialize_topup_packages(settings, tariff, config.topup_packages_for(tariff), lang)
            if topup_kind in {"all", "regular"}
            else []
        )
        premium_plans = (
            _serialize_topup_packages(
                settings,
                tariff,
                tariff.premium_topup_packages,
                lang,
                sale_mode="premium_topup",
                title_prefix=f"{tariff.premium_name(lang)} ",
            )
            if topup_kind in {"all", "premium"} and tariff.premium_squad_uuids
            else []
        )
        premium_bonus_bytes = int(getattr(sub, "premium_bonus_bytes", 0) or 0)
        premium_unlimited_override = bool(getattr(sub, "premium_unlimited_override", False))
        premium_limit_bytes = (
            int(sub.premium_baseline_bytes or 0)
            + int(sub.premium_topup_balance_bytes or 0)
            + int(getattr(sub, "premium_topup_used_bytes", 0) or 0)
            + premium_bonus_bytes
        )
        premium_access = await request.app["subscription_service"].premium_access_for_tariff(tariff)
        return web.json_response(
            {
                "ok": True,
                "tariff_key": tariff.key,
                "tariff_name": tariff.name(lang),
                "topup_kind": topup_kind,
                "premium_title": tariff.premium_name(lang),
                "traffic_percent": _traffic_percent(
                    sub.traffic_used_bytes, sub.traffic_limit_bytes
                ),
                "premium_traffic_percent": 0
                if premium_unlimited_override
                else _traffic_percent(
                    sub.premium_used_bytes,
                    premium_limit_bytes,
                ),
                "premium_limit_bytes": premium_limit_bytes,
                "premium_used_bytes": int(sub.premium_used_bytes or 0),
                "premium_baseline_bytes": int(sub.premium_baseline_bytes or 0),
                "premium_topup_balance_bytes": int(sub.premium_topup_balance_bytes or 0),
                "premium_topup_used_bytes": int(getattr(sub, "premium_topup_used_bytes", 0) or 0),
                "premium_bonus_bytes": premium_bonus_bytes,
                "premium_unlimited_override": premium_unlimited_override,
                "premium_is_limited": bool(sub.premium_is_limited),
                "premium_squad_labels": premium_access.get("squad_labels") or [],
                "premium_node_labels": premium_access.get("node_labels") or [],
                "warning_levels": settings.tariff_traffic_warning_levels,
                "plans": plans + premium_plans,
            }
        )


async def tariff_change_options_route(request: web.Request) -> web.Response:
    user_id = _require_user_id(request)
    settings: Settings = request.app["settings"]
    config = settings.tariffs_config
    if not config:
        return _json_error(404, "tariffs_unavailable", "Tariffs are not configured")

    async_session_factory: sessionmaker = request.app["async_session_factory"]
    subscription_service: SubscriptionService = request.app["subscription_service"]
    async with async_session_factory() as session:
        db_user = await user_dal.get_user_by_id(session, user_id)
        if not db_user or db_user.is_banned:
            return _json_error(403, "access_denied", "Access denied")
        sub = await subscription_dal.get_active_subscription_by_user_id(
            session, user_id, db_user.panel_user_uuid
        )
        if not sub or not sub.tariff_key:
            return _json_error(
                400, "subscription_required", "Active tariff subscription is required"
            )
        lang = db_user.language_code or settings.DEFAULT_LANGUAGE
        current = config.require(sub.tariff_key)
        targets = []
        for tariff in config.enabled_tariffs:
            if tariff.key == current.key:
                continue
            options = await subscription_service.calculate_tariff_switch_options_with_hwid(
                session, sub, tariff
            )
            targets.append(_serialize_tariff_change_target(settings, config, tariff, options, lang))
        return web.json_response(
            {
                "ok": True,
                "current": {
                    "tariff_key": current.key,
                    "title": current.name(lang),
                    "description": current.description(lang),
                    "billing_model": current.billing_model,
                },
                "targets": targets,
            }
        )


async def tariff_change_route(request: web.Request) -> web.Response:
    user_id = _require_user_id(request)
    payload = await _read_json(request)
    change_payload, validation_error = _validate_model_payload(WebAppTariffChangePayload, payload)
    if validation_error:
        return validation_error
    mode = str(change_payload.mode or "").strip()
    if mode not in {"recalc_days", "convert_days_to_gb"}:
        return _json_error(400, "invalid_change_mode", "This tariff change requires payment")

    settings: Settings = request.app["settings"]
    if not settings.tariffs_config:
        return _json_error(404, "tariffs_unavailable", "Tariffs are not configured")
    async_session_factory: sessionmaker = request.app["async_session_factory"]
    subscription_service: SubscriptionService = request.app["subscription_service"]
    async with async_session_factory() as session:
        db_user = await user_dal.get_user_by_id(session, user_id)
        if not db_user or db_user.is_banned:
            return _json_error(403, "access_denied", "Access denied")
        result = await subscription_service.switch_tariff_without_payment(
            session,
            user_id,
            str(change_payload.tariff_key),
            mode,
        )
        if not result:
            await session.rollback()
            return _json_error(400, "change_failed", "Tariff change failed")
        await session.commit()
        return web.json_response({"ok": True, **result})


async def tariff_change_payment_route(request: web.Request) -> web.Response:
    user_id = _require_user_id(request)
    payload = await _read_json(request)
    payment_payload, validation_error = _validate_model_payload(WebAppPaymentCreatePayload, payload)
    if validation_error:
        return validation_error
    method = str(payment_payload.method or "").strip().lower()
    tariff_key = str(payment_payload.tariff_key or "").strip()
    settings: Settings = request.app["settings"]
    config = settings.tariffs_config
    default_currency_code = default_payment_currency_code_for_settings(settings)
    if not config:
        return _json_error(404, "tariffs_unavailable", "Tariffs are not configured")
    if not tariff_key:
        return _json_error(400, "invalid_plan", "Tariff is not selected")

    async_session_factory: sessionmaker = request.app["async_session_factory"]
    subscription_service: SubscriptionService = request.app["subscription_service"]
    async with async_session_factory() as session:
        db_user = await user_dal.get_user_by_id(session, user_id)
        if not db_user or db_user.is_banned:
            return _json_error(403, "access_denied", "Access denied")
        sub = await subscription_dal.get_active_subscription_by_user_id(
            session, user_id, db_user.panel_user_uuid
        )
        if not sub:
            return _json_error(
                400, "subscription_required", "Active tariff subscription is required"
            )
        target = config.require(tariff_key)
        options = await subscription_service.calculate_tariff_switch_options_with_hwid(
            session, sub, target
        )
        price = float(options.get("paid_diff_rub") or 0)
        if price <= 0:
            return _json_error(
                400, "payment_not_required", "Payment is not required for this tariff change"
            )
        return await _create_subscription_payment(
            request=request,
            session=session,
            user_id=user_id,
            method=method,
            months=1,
            price=price,
            stars_price=None,
            currency=default_currency_code,
            lang=db_user.language_code or settings.DEFAULT_LANGUAGE,
            sale_mode=f"tariff_upgrade@{target.key}",
        )


async def device_topup_options_route(request: web.Request) -> web.Response:
    user_id = _require_user_id(request)
    settings: Settings = request.app["settings"]
    config = settings.tariffs_config
    if not settings.MY_DEVICES_SECTION_ENABLED:
        return _json_error(404, "devices_disabled", "Devices section is disabled")
    if not config:
        return _json_error(404, "tariffs_unavailable", "Tariffs are not configured")

    async_session_factory: sessionmaker = request.app["async_session_factory"]
    subscription_service: SubscriptionService = request.app["subscription_service"]
    async with async_session_factory() as session:
        db_user = await user_dal.get_user_by_id(session, user_id)
        if not db_user or db_user.is_banned:
            return _json_error(403, "access_denied", "Access denied")
        sub = await subscription_dal.get_active_subscription_by_user_id(
            session, user_id, db_user.panel_user_uuid
        )
        if not sub or not sub.tariff_key:
            return _json_error(
                400, "subscription_required", "Active tariff subscription is required"
            )
        tariff = config.require(sub.tariff_key)
        if tariff.billing_model != "period":
            return _json_error(400, "device_topup_unavailable", "Device top-up is not available")
        lang = db_user.language_code or settings.DEFAULT_LANGUAGE
        active = await subscription_service.get_active_subscription_details(session, user_id)
        renewal_available = bool(active and active.get("device_topup_renewal_available"))
        extra_hwid_valid_until = active.get("extra_hwid_devices_valid_until") if active else None
        extra_hwid_valid_until_text = (
            active.get("extra_hwid_devices_valid_until_text") if active else None
        ) or _billing_datetime_text(extra_hwid_valid_until)
        packages = tariff.hwid_device_packages
        default_currency = default_currency_key_for_settings(settings)
        default_currency_code = payment_currency_code(default_currency)
        if packages and hasattr(packages, "for_currency"):
            default_packages = packages.for_currency(default_currency)
        else:
            default_packages = getattr(packages, default_currency, []) if packages else []
        currency_counts = {int(package.count) for package in default_packages}
        stars_counts = {int(package.count) for package in (packages.stars if packages else [])}
        plans = []
        for count in sorted(currency_counts | stars_counts):
            currency_quote = (
                await subscription_service.quote_hwid_device_topup(
                    session,
                    user_id=user_id,
                    device_count=count,
                    tariff_key=tariff.key,
                    renewal=renewal_available,
                    currency=default_currency,
                )
                if count in currency_counts
                else None
            )
            stars_quote = (
                await subscription_service.quote_hwid_device_topup(
                    session,
                    user_id=user_id,
                    device_count=count,
                    tariff_key=tariff.key,
                    renewal=renewal_available,
                    currency="stars",
                )
                if count in stars_counts
                else None
            )
            if not currency_quote and not stars_quote:
                continue
            sale_mode_for_plan = "hwid_devices_renewal" if renewal_available else "hwid_devices"
            plan = {
                "id": f"{tariff.key}:hwid:{count}{':renewal' if renewal_available else ''}",
                "tariff_key": tariff.key,
                "tariff_name": tariff.name(lang),
                "billing_model": tariff.billing_model,
                "sale_mode": sale_mode_for_plan,
                "months": count,
                "device_count": count,
                "price": float(currency_quote.get("price") if currency_quote else 0),
                "currency": default_currency_code,
                "title": f"+{count}",
                "subtitle": tariff.name(lang),
                "valid_from": _billing_iso_datetime(
                    (currency_quote or stars_quote).get("valid_from")
                ),
                "valid_until": _billing_iso_datetime(
                    (currency_quote or stars_quote).get("valid_until")
                ),
                "proration_ratio": float(
                    (currency_quote or stars_quote).get("proration_ratio") or 0
                ),
            }
            if stars_quote and int(stars_quote.get("price") or 0) > 0:
                plan["stars_price"] = int(stars_quote["price"])
            plans.append(plan)
        return web.json_response(
            {
                "ok": True,
                "tariff_key": tariff.key,
                "tariff_name": tariff.name(lang),
                "current_limit": _coerce_int_or_none(active.get("max_devices")) if active else None,
                "extra_hwid_devices": int(active.get("extra_hwid_devices") or 0)
                if active
                else int(sub.extra_hwid_devices or 0),
                "extra_hwid_devices_valid_until": _billing_iso_datetime(extra_hwid_valid_until),
                "extra_hwid_devices_valid_until_text": extra_hwid_valid_until_text,
                "renewal_available": renewal_available,
                "renewal_recommended_count": int(active.get("extra_hwid_devices") or 0)
                if active and renewal_available
                else 0,
                "plans": plans,
            }
        )


def _yookassa_payment_payload_for_processing(payload: Dict[str, Any]) -> Dict[str, Any]:
    normalized = dict(payload or {})
    if not isinstance(normalized.get("amount"), dict):
        amount_value = normalized.get("amount_value")
        amount_currency = normalized.get("amount_currency")
        if amount_value is not None or amount_currency:
            normalized["amount"] = {
                "value": str(amount_value if amount_value is not None else 0),
                "currency": amount_currency or "RUB",
            }
    return normalized


def _payment_status_can_be_refreshed(payment: Payment) -> bool:
    normalized = str(getattr(payment, "status", "") or "").lower()
    if normalized == "succeeded":
        return False
    if normalized in {"failed", "canceled", "cancelled", "failed_creation"}:
        return False
    return normalized.startswith("pending") or normalized in {"waiting_for_capture", "created"}


async def _refresh_yookassa_payment_status(
    request: web.Request,
    session: AsyncSession,
    payment: Payment,
) -> Payment:
    if str(getattr(payment, "provider", "") or "").lower() != "yookassa":
        return payment
    if not _payment_status_can_be_refreshed(payment):
        return payment

    yookassa_payment_id = payment.yookassa_payment_id or payment.provider_payment_id
    yookassa_service = request.app.get("yookassa_service")
    if (
        not yookassa_payment_id
        or not yookassa_service
        or not getattr(yookassa_service, "configured", False)
        or not hasattr(yookassa_service, "get_payment_info")
    ):
        return payment

    try:
        provider_payload = await yookassa_service.get_payment_info(yookassa_payment_id)
    except Exception:
        logger.exception("Failed to refresh YooKassa payment %s status", payment.payment_id)
        return payment

    if not provider_payload:
        return payment

    provider_payload = _yookassa_payment_payload_for_processing(provider_payload)
    provider_status = str(provider_payload.get("status") or "").lower()
    if provider_status == "succeeded" and provider_payload.get("paid") is True:
        from bot.payment_providers.yookassa import (
            payment_processing_lock,
            process_successful_payment,
        )

        async with payment_processing_lock:
            current = await payment_dal.get_payment_by_db_id(session, payment.payment_id)
            if not current:
                return payment
            if current.status == "succeeded":
                return current
            try:
                await process_successful_payment(
                    session,
                    request.app["bot"],
                    provider_payload,
                    request.app["i18n"],
                    request.app["settings"],
                    request.app["panel_service"],
                    request.app["subscription_service"],
                    request.app["referral_service"],
                    request.app.get("lknpd_service"),
                )
                await session.commit()
            except Exception:
                await session.rollback()
                logger.exception(
                    "Failed to process refreshed YooKassa payment %s",
                    payment.payment_id,
                )
                return current
            return await payment_dal.get_payment_by_db_id(session, payment.payment_id) or current

    if provider_status in {"canceled", "cancelled"}:
        from bot.payment_providers.yookassa import (
            payment_processing_lock,
            process_cancelled_payment,
        )

        async with payment_processing_lock:
            current = await payment_dal.get_payment_by_db_id(session, payment.payment_id)
            if not current:
                return payment
            if not _payment_status_can_be_refreshed(current):
                return current
            try:
                await process_cancelled_payment(
                    session,
                    request.app["bot"],
                    provider_payload,
                    request.app["i18n"],
                    request.app["settings"],
                )
                await session.commit()
            except Exception:
                await session.rollback()
                logger.exception(
                    "Failed to process refreshed cancelled YooKassa payment %s",
                    payment.payment_id,
                )
                return current
            return await payment_dal.get_payment_by_db_id(session, payment.payment_id) or current

    return payment


async def _refresh_wata_payment_status(
    request: web.Request,
    session: AsyncSession,
    payment: Payment,
) -> Payment:
    if str(getattr(payment, "provider", "") or "").lower() != "wata":
        return payment
    if not _payment_status_can_be_refreshed(payment):
        return payment

    wata_service = request.app.get("wata_service")
    if (
        not wata_service
        or not getattr(wata_service, "configured", False)
        or not hasattr(wata_service, "refresh_payment_status")
    ):
        return payment

    try:
        return await wata_service.refresh_payment_status(session, payment)
    except Exception:
        logger.exception("Failed to refresh Wata payment %s status", payment.payment_id)
        return payment


async def payment_status_route(request: web.Request) -> web.Response:
    user_id = _require_user_id(request)
    try:
        payment_id = int(request.match_info["payment_id"])
    except (TypeError, ValueError):
        return _json_error(400, "invalid_payment", "Invalid payment id")

    async_session_factory: sessionmaker = request.app["async_session_factory"]
    async with async_session_factory() as session:
        payment = await payment_dal.get_payment_by_db_id(session, payment_id)
        if not payment or payment.user_id != user_id:
            return _json_error(404, "not_found", "Payment not found")
        payment = await _refresh_yookassa_payment_status(request, session, payment)
        payment = await _refresh_wata_payment_status(request, session, payment)
        if payment.status == "succeeded":
            await invalidate_webapp_user_caches(request.app["settings"], user_id)
        return web.json_response(
            {
                "ok": True,
                "payment_id": payment.payment_id,
                "status": payment.status,
                "paid": payment.status == "succeeded",
            }
        )


def _sale_mode_base(sale_mode: str) -> str:
    return str(sale_mode or "subscription").split("@", 1)[0].split("|", 1)[0]


def _sale_mode_tariff_key(sale_mode: str) -> Optional[str]:
    if "@" not in str(sale_mode or ""):
        return None
    return str(sale_mode).split("@", 1)[1].split("|", 1)[0] or None


def _sale_mode_is_traffic(sale_mode: str) -> bool:
    return _sale_mode_base(sale_mode) in {"traffic", "traffic_package", "topup", "premium_topup"}


def _sale_mode_is_hwid_devices(sale_mode: str) -> bool:
    return _sale_mode_base(sale_mode) in {
        "hwid_device",
        "hwid_devices",
        "hwid_devices_renewal",
    }


async def _create_subscription_payment(
    *,
    request: web.Request,
    session: AsyncSession,
    user_id: int,
    method: str,
    months: Any,
    price: float,
    stars_price: Optional[int],
    lang: str,
    currency: Optional[str] = None,
    sale_mode: str = "subscription",
    traffic_gb: Optional[float] = None,
    is_admin: bool = False,
    hwid_quote: Optional[Dict[str, Any]] = None,
) -> web.Response:
    settings: Settings = request.app["settings"]
    payment_currency = (currency or default_payment_currency_code_for_settings(settings)).upper()
    sale_mode = str(sale_mode or "subscription")
    traffic_sale = _sale_mode_is_traffic(sale_mode)
    hwid_devices_sale = _sale_mode_is_hwid_devices(sale_mode)
    description = (
        _traffic_payment_description(float(traffic_gb if traffic_gb is not None else months), lang)
        if traffic_sale
        else _hwid_devices_payment_description(int(float(months)), lang)
        if hwid_devices_sale
        else _payment_description(int(months), lang)
    )

    from bot.payment_providers import WebAppPaymentContext, get_provider_spec

    provider_spec = get_provider_spec(method)
    if provider_spec and provider_spec.create_webapp_payment:
        if not provider_spec.is_visible_for_user(settings, request.app, is_admin=is_admin):
            logger.warning(
                "WebApp payment method unavailable: method=%s enabled=%s configured=%s",
                method,
                provider_spec.is_effectively_enabled(settings),
                provider_spec.is_service_configured(request.app),
            )
            return _json_error(400, "payment_unavailable", "Payment method unavailable")
        if not provider_spec.is_usable_for_payment_currency(settings, payment_currency):
            logger.warning(
                "WebApp payment method does not support currency: method=%s currency=%s",
                method,
                payment_currency,
            )
            return _json_error(
                400,
                "unsupported_currency",
                "Payment method does not support this currency",
            )
        return await provider_spec.create_webapp_payment(
            WebAppPaymentContext(
                request=request,
                session=session,
                user_id=user_id,
                method=method,
                months=months,
                price=price,
                stars_price=stars_price,
                currency=payment_currency,
                description=description,
                sale_mode=sale_mode,
                traffic_gb=traffic_gb,
                hwid_valid_from=hwid_quote.get("valid_from") if hwid_quote else None,
                hwid_valid_until=hwid_quote.get("valid_until") if hwid_quote else None,
                hwid_pricing_period_months=hwid_quote.get("pricing_period_months")
                if hwid_quote
                else None,
                hwid_proration_ratio=hwid_quote.get("proration_ratio") if hwid_quote else None,
                hwid_full_price=hwid_quote.get("full_price") if hwid_quote else None,
            )
        )

    return _json_error(400, "payment_unavailable", "Payment method unavailable")
