"""
Payments Plugin for EZ-Input

Handles checkout sessions (Stripe, Airwallex, ERC20), provider webhooks,
ERC20 transfer verification, wallet balances, and ERC credit spending.
"""

from __future__ import annotations

import json
import time
import uuid
from pathlib import Path
from typing import Any

import httpx
from fastapi import APIRouter, FastAPI, HTTPException, Request
from pydantic import BaseModel, Field

from plugin import Plugin

DATA_DIR = Path(__file__).resolve().parent.parent / "data"
DATA_DIR.mkdir(exist_ok=True)
PAYMENTS_FILE = DATA_DIR / "payments.json"

PRODUCTS = {
    "10-life-phase": {"name": "10 Life Phase", "price_usd": 44, "currency": "usd"},
    "karma": {"name": "Karma", "price_usd": 33, "currency": "usd"},
    "contracts": {"name": "Contracts", "price_usd": 55, "currency": "usd"},
    # AngelZen one-time tiers (HKD)
    "angelzen-tier-100": {"name": "AngelZen · Essential Guidance", "price_hkd": 100, "currency": "hkd"},
    "angelzen-tier-500": {"name": "AngelZen · Group Practice Session", "price_hkd": 500, "currency": "hkd"},
    "angelzen-tier-1000": {"name": "AngelZen · Deep Teaching Day", "price_hkd": 1000, "currency": "hkd"},
    "angelzen-item-incense": {"name": "AngelZen · Purified Incense", "price_hkd": 180, "currency": "hkd"},
    # YulianTarot one-time tiers (HKD)
    "yuliantarot-tier-100": {"name": "YulianTarot · Single Card Reading", "price_hkd": 100, "currency": "hkd"},
    "yuliantarot-tier-500": {"name": "YulianTarot · Three-Card Spread", "price_hkd": 500, "currency": "hkd"},
    "yuliantarot-tier-1000": {"name": "YulianTarot · Full Life Spread", "price_hkd": 1000, "currency": "hkd"},
    "yuliantarot-item-crystal": {"name": "YulianTarot · Ritual Crystal", "price_hkd": 220, "currency": "hkd"},
}

SUBSCRIPTIONS = {
    "angelzen-subscription-10k": {
        "name": "AngelZen · Monthly Group Practice",
        "price_hkd": 10000,
        "currency": "hkd",
        "interval": "month",
    },
    "yuliantarot-subscription-10k": {
        "name": "YulianTarot · Monthly Tarot Circle",
        "price_hkd": 10000,
        "currency": "hkd",
        "interval": "month",
    },
}

PLATFORM_FEE_RATE = 0.05


def _load_store() -> dict:
    if not PAYMENTS_FILE.exists():
        return {"orders": {}, "balances": {}, "credit_txs": [], "subscriptions": {}}
    try:
        return json.loads(PAYMENTS_FILE.read_text(encoding="utf-8"))
    except (json.JSONDecodeError, OSError):
        return {"orders": {}, "balances": {}, "credit_txs": [], "subscriptions": {}}


def _save_store(store: dict) -> None:
    PAYMENTS_FILE.write_text(json.dumps(store, indent=2, ensure_ascii=False), encoding="utf-8")


def _rate(config: dict) -> dict:
    erc_rate = float(config.get("erc_rate") or "10")
    return {"usd_per_erc": 1.0 / erc_rate, "erc_per_usd": erc_rate}


def _amount_erc(amount_usd: float, config: dict) -> float:
    return amount_usd * _rate(config)["erc_per_usd"]


def _new_order_id() -> str:
    return f"ord_{uuid.uuid4().hex[:16]}"


def _new_subscription_id() -> str:
    return f"sub_{uuid.uuid4().hex[:16]}"


def _new_tx_id() -> str:
    return f"ctx_{uuid.uuid4().hex[:16]}"


def _platform_fee(amount: float) -> float:
    return round(amount * PLATFORM_FEE_RATE, 2)


def _credit_balance(store: dict, user_id: str, amount: float) -> None:
    store["balances"][user_id] = store["balances"].get(user_id, 0) + amount


def _ltm_url(config: dict) -> str | None:
    return config.get("ltm_url")


def _ltm_post(url: str, path: str, payload: dict) -> None:
    try:
        httpx.post(f"{url.rstrip('/')}{path}", json=payload, timeout=5.0)
    except Exception:
        # LTM sync is best-effort; don't fail the payment flow
        pass


def _ltm_sync_order(order: dict, config: dict) -> None:
    url = _ltm_url(config)
    if not url:
        return
    order_id = order.get("order_id") or order.get("subscription_id")
    _ltm_post(
        url,
        "/payment/order",
        {
            "order_id": order_id,
            "user_id": order["user_id"],
            "product_id": order["product_id"],
            "method": order["method"],
            "currency": order.get("currency", "usd"),
            "amount_usd": order.get("amount_usd"),
            "amount_hkd": order.get("amount_hkd"),
            "erc_amount": order["erc_amount"],
            "status": order["status"],
            "payload": {
                "provider_tx_id": order.get("provider_tx_id"),
                "tx_hash": order.get("tx_hash"),
                "created_at": order.get("created_at"),
            },
        },
    )


def _ltm_sync_wallet(user_id: str, balance: float, tx: dict, config: dict) -> None:
    url = _ltm_url(config)
    if not url:
        return
    _ltm_post(
        url,
        f"/payment/wallet/{user_id}/tx",
        {
            "tx_id": tx["tx_id"],
            "amount": tx["amount"],
            "reason": tx["reason"],
            "balance_after": balance,
        },
    )


def _ltm_sync_paid(order: dict, store: dict, config: dict) -> None:
    _ltm_sync_order(order, config)
    user_id = order["user_id"]
    balance = store["balances"].get(user_id, 0)
    _ltm_sync_wallet(
        user_id,
        balance,
        {
            "tx_id": _new_tx_id(),
            "amount": order["erc_amount"],
            "reason": f"purchase:{order['product_id']}",
        },
        config,
    )


class CheckoutPayload(BaseModel):
    product_id: str
    user_id: str
    currency: str = "usd"
    method: str


class ERC20VerifyPayload(BaseModel):
    order_id: str
    tx_hash: str


class UseCreditsPayload(BaseModel):
    user_id: str
    amount: float = Field(..., gt=0)
    reason: str


class SubscribePayload(BaseModel):
    subscription_id: str
    user_id: str
    method: str


class PaymentsPlugin(Plugin):
    name = "payments"
    version = "1.0.0"
    description = "Payments, ERC credits, and wallet for EZ-Input upgrades."

    def register(self, app: FastAPI) -> None:
        config = self.context.get("payments_config", {})

        router = APIRouter(prefix="/payments", tags=["payments"])

        @router.get("/rate")
        async def get_rate() -> dict:
            return _rate(config)

        @router.post("/checkout")
        async def checkout(payload: CheckoutPayload) -> dict:
            if payload.product_id not in PRODUCTS:
                raise HTTPException(status_code=400, detail="Unknown product_id")
            if payload.method not in {"stripe", "airwallex", "erc20"}:
                raise HTTPException(status_code=400, detail="Unsupported payment method")

            product = PRODUCTS[payload.product_id]
            currency = product.get("currency", "usd")
            if payload.currency != currency:
                raise HTTPException(status_code=400, detail=f"Currency mismatch: product is priced in {currency}")

            if currency == "hkd":
                if payload.method == "erc20":
                    raise HTTPException(status_code=400, detail="ERC20 is only supported for USD products")
                amount_hkd = product["price_hkd"]
                platform_fee_hkd = _platform_fee(amount_hkd)
                amount_usd = None
                erc_amount = 0.0
            else:
                amount_usd = product["price_usd"]
                platform_fee_usd = _platform_fee(amount_usd)
                amount_hkd = None
                erc_amount = _amount_erc(amount_usd, config)

            order_id = _new_order_id()

            store = _load_store()
            order = {
                "order_id": order_id,
                "user_id": payload.user_id,
                "product_id": payload.product_id,
                "method": payload.method,
                "currency": currency,
                "erc_amount": erc_amount,
                "status": "pending",
                "created_at": time.time(),
                "provider_tx_id": None,
                "tx_hash": None,
            }
            if currency == "hkd":
                order["amount_hkd"] = amount_hkd
                order["platform_fee_hkd"] = platform_fee_hkd
            else:
                order["amount_usd"] = amount_usd
                order["platform_fee_usd"] = platform_fee_usd
            store["orders"][order_id] = order
            _save_store(store)
            _ltm_sync_order(order, config)

            if payload.method == "stripe":
                session_url = await _stripe_checkout(config, order_id, product, currency)
            elif payload.method == "airwallex":
                session_url = await _airwallex_checkout(config, order_id, product, currency)
            else:
                return _erc20_response(config, order_id, amount_usd, erc_amount)

            response = {
                "order_id": order_id,
                "status": "pending",
                "method": payload.method,
                "currency": currency,
                "erc_amount": erc_amount,
                "session_url": session_url,
                "pay_address": None,
                "contract_address": None,
                "rate": _rate(config),
            }
            if currency == "hkd":
                response["amount_hkd"] = amount_hkd
                response["platform_fee_hkd"] = platform_fee_hkd
            else:
                response["amount_usd"] = amount_usd
                response["platform_fee_usd"] = platform_fee_usd
            return response

        async def _stripe_checkout(config: dict, order_id: str, product: dict, currency: str) -> str | None:
            if not config.get("stripe_secret_key"):
                return f"/api/payments/simulate-callback?order_id={order_id}&redirect=/index.html"

            try:
                import stripe
            except ImportError as exc:  # pragma: no cover
                raise HTTPException(status_code=500, detail=f"stripe package not installed: {exc}")

            stripe.api_key = config["stripe_secret_key"]
            public_host = config.get("public_host", "https://ez-input.com").rstrip("/")

            amount = product["price_hkd"] if currency == "hkd" else product["price_usd"]

            try:
                session = stripe.checkout.Session.create(
                    payment_method_types=["card"],
                    line_items=[
                        {
                            "price_data": {
                                "currency": currency.lower(),
                                "product_data": {"name": product["name"]},
                                "unit_amount": int(amount * 100),
                            },
                            "quantity": 1,
                        }
                    ],
                    mode="payment",
                    success_url=f"{public_host}/api/payments/simulate-callback?order_id={order_id}&redirect=/index.html",
                    cancel_url=f"{public_host}/api/payments/simulate-callback?order_id={order_id}&canceled=1&redirect=/index.html",
                )
            except Exception as exc:
                raise HTTPException(status_code=502, detail=f"Stripe error: {exc}")

            store = _load_store()
            store["orders"][order_id]["provider_tx_id"] = session.id
            _save_store(store)
            _ltm_sync_order(store["orders"][order_id], config)
            return session.url

        async def _airwallex_checkout(config: dict, order_id: str, product: dict, currency: str) -> str | None:
            if not config.get("airwallex_api_key") or not config.get("airwallex_client_id"):
                return f"/api/payments/simulate-callback?order_id={order_id}&redirect=/index.html"

            amount = product["price_hkd"] if currency == "hkd" else product["price_usd"]

            try:
                async with httpx.AsyncClient() as client:
                    auth_resp = await client.post(
                        "https://api.airwallex.com/api/v1/authentication/login",
                        headers={
                            "x-api-key": config["airwallex_api_key"],
                            "x-client-id": config["airwallex_client_id"],
                        },
                    )
                    auth_resp.raise_for_status()
                    token = auth_resp.json().get("token")

                    payment_resp = await client.post(
                        "https://api.airwallex.com/api/v1/pa/payment_intents/create",
                        headers={"Authorization": f"Bearer {token}"},
                        json={
                            "request_id": order_id,
                            "amount": amount,
                            "currency": currency.upper(),
                            "merchant_order_id": order_id,
                        },
                    )
                    payment_resp.raise_for_status()
                    payment_data = payment_resp.json()
            except Exception as exc:
                raise HTTPException(status_code=502, detail=f"Airwallex error: {exc}")

            store = _load_store()
            store["orders"][order_id]["provider_tx_id"] = payment_data.get("id")
            _save_store(store)
            _ltm_sync_order(store["orders"][order_id], config)

            return payment_data.get("client_secret") or f"/api/payments/simulate-callback?order_id={order_id}&redirect=/index.html"

        async def _stripe_subscription_checkout(config: dict, subscription_id: str, subscription: dict, amount_hkd: float) -> str | None:
            if not config.get("stripe_secret_key"):
                return f"/api/payments/simulate-callback?subscription_id={subscription_id}&redirect=/index.html"
            try:
                import stripe
            except ImportError as exc:
                raise HTTPException(status_code=500, detail=f"stripe package not installed: {exc}")

            stripe.api_key = config["stripe_secret_key"]
            public_host = config.get("public_host", "https://ez-input.com").rstrip("/")

            try:
                session = stripe.checkout.Session.create(
                    payment_method_types=["card"],
                    line_items=[
                        {
                            "price_data": {
                                "currency": subscription["currency"].lower(),
                                "product_data": {"name": subscription["name"]},
                                "unit_amount": int(amount_hkd * 100),
                                "recurring": {"interval": subscription["interval"]},
                            },
                            "quantity": 1,
                        }
                    ],
                    mode="subscription",
                    success_url=f"{public_host}/api/payments/simulate-callback?subscription_id={subscription_id}&redirect=/index.html",
                    cancel_url=f"{public_host}/api/payments/simulate-callback?subscription_id={subscription_id}&canceled=1&redirect=/index.html",
                )
            except Exception as exc:
                raise HTTPException(status_code=502, detail=f"Stripe error: {exc}")

            store = _load_store()
            store["subscriptions"][subscription_id]["provider_tx_id"] = session.id
            _save_store(store)
            return session.url

        async def _airwallex_subscription_checkout(config: dict, subscription_id: str, subscription: dict, amount_hkd: float) -> str | None:
            if not config.get("airwallex_api_key") or not config.get("airwallex_client_id"):
                return f"/api/payments/simulate-callback?subscription_id={subscription_id}&redirect=/index.html"
            try:
                async with httpx.AsyncClient() as client:
                    auth_resp = await client.post(
                        "https://api.airwallex.com/api/v1/authentication/login",
                        headers={
                            "x-api-key": config["airwallex_api_key"],
                            "x-client-id": config["airwallex_client_id"],
                        },
                    )
                    auth_resp.raise_for_status()
                    token = auth_resp.json().get("token")

                    payment_resp = await client.post(
                        "https://api.airwallex.com/api/v1/pa/payment_intents/create",
                        headers={"Authorization": f"Bearer {token}"},
                        json={
                            "request_id": subscription_id,
                            "amount": amount_hkd,
                            "currency": subscription["currency"].upper(),
                            "merchant_order_id": subscription_id,
                        },
                    )
                    payment_resp.raise_for_status()
                    payment_data = payment_resp.json()
            except Exception as exc:
                raise HTTPException(status_code=502, detail=f"Airwallex error: {exc}")

            store = _load_store()
            store["subscriptions"][subscription_id]["provider_tx_id"] = payment_data.get("id")
            _save_store(store)
            return payment_data.get("client_secret") or f"/api/payments/simulate-callback?subscription_id={subscription_id}&redirect=/index.html"

        @router.post("/subscribe")
        async def subscribe(payload: SubscribePayload) -> dict:
            if payload.subscription_id not in SUBSCRIPTIONS:
                raise HTTPException(status_code=400, detail="Unknown subscription_id")
            if payload.method not in {"stripe", "airwallex"}:
                raise HTTPException(status_code=400, detail="Unsupported payment method")

            subscription = SUBSCRIPTIONS[payload.subscription_id]
            amount_hkd = subscription["price_hkd"]
            platform_fee_hkd = _platform_fee(amount_hkd)
            subscription_id = _new_subscription_id()

            store = _load_store()
            store.setdefault("subscriptions", {})[subscription_id] = {
                "subscription_id": subscription_id,
                "user_id": payload.user_id,
                "product_id": payload.subscription_id,
                "method": payload.method,
                "amount_hkd": amount_hkd,
                "platform_fee_hkd": platform_fee_hkd,
                "currency": subscription["currency"],
                "interval": subscription["interval"],
                "status": "pending",
                "created_at": time.time(),
                "provider_tx_id": None,
            }
            _save_store(store)

            if payload.method == "stripe":
                session_url = await _stripe_subscription_checkout(config, subscription_id, subscription, amount_hkd)
            else:
                session_url = await _airwallex_subscription_checkout(config, subscription_id, subscription, amount_hkd)

            return {
                "subscription_id": subscription_id,
                "status": "pending",
                "method": payload.method,
                "amount_hkd": amount_hkd,
                "platform_fee_hkd": platform_fee_hkd,
                "session_url": session_url,
            }

        def _erc20_response(config: dict, order_id: str, amount_usd: float, erc_amount: float) -> dict:
            pay_address = config.get("merchant_wallet_address")
            contract_address = config.get("erc20_contract_address")
            if not pay_address or not contract_address:
                pay_address = pay_address or "0xSimulatedMerchantAddress"
                contract_address = contract_address or "0xSimulatedContractAddress"

            return {
                "order_id": order_id,
                "status": "pending",
                "method": "erc20",
                "amount_usd": amount_usd,
                "erc_amount": erc_amount,
                "session_url": None,
                "pay_address": pay_address,
                "contract_address": contract_address,
                "rate": _rate(config),
            }

        @router.get("/simulate-callback")
        async def simulate_callback(
            order_id: str | None = None,
            subscription_id: str | None = None,
            canceled: str | None = None,
            redirect: str | None = None,
        ) -> dict:
            """Demo-only callback that marks simulated orders or subscriptions as paid."""
            store = _load_store()

            if subscription_id:
                sub = store.get("subscriptions", {}).get(subscription_id)
                if not sub:
                    if redirect:
                        from fastapi.responses import RedirectResponse

                        return RedirectResponse(f"{redirect}?subscription_id={subscription_id}&error=not_found")
                    raise HTTPException(status_code=404, detail="Subscription not found")
                if canceled:
                    sub["status"] = "canceled"
                    _save_store(store)
                    if redirect:
                        from fastapi.responses import RedirectResponse

                        return RedirectResponse(f"{redirect}?subscription_id={subscription_id}&status=canceled")
                    return {"subscription_id": subscription_id, "status": "canceled", "simulated": True}
                if sub["status"] != "active":
                    sub["status"] = "active"
                    _save_store(store)
                    _ltm_sync_order(sub, config)
                if redirect:
                    from fastapi.responses import RedirectResponse

                    return RedirectResponse(f"{redirect}?subscription_id={subscription_id}&status=active")
                return {"subscription_id": subscription_id, "status": "active", "simulated": True}

            order = store["orders"].get(order_id)
            if not order:
                if redirect:
                    from fastapi.responses import RedirectResponse

                    return RedirectResponse(f"{redirect}?order_id={order_id}&error=not_found")
                raise HTTPException(status_code=404, detail="Order not found")
            if canceled:
                order["status"] = "canceled"
                _save_store(store)
                _ltm_sync_order(order, config)
                if redirect:
                    from fastapi.responses import RedirectResponse

                    return RedirectResponse(f"{redirect}?order_id={order_id}&status=canceled")
                return {"order_id": order_id, "status": "canceled", "simulated": True}
            if order["status"] != "paid":
                order["status"] = "paid"
                _credit_balance(store, order["user_id"], order["erc_amount"])
                _save_store(store)
                _ltm_sync_paid(order, store, config)
            if redirect:
                from fastapi.responses import RedirectResponse

                return RedirectResponse(f"{redirect}?order_id={order_id}&status=paid")
            return {
                "order_id": order_id,
                "status": "paid",
                "credits_added": order["erc_amount"],
                "balance": store["balances"].get(order["user_id"], 0),
                "simulated": True,
            }

        @router.post("/webhook/{provider}")
        async def webhook(provider: str, request: Request) -> dict:
            if provider not in {"stripe", "airwallex"}:
                raise HTTPException(status_code=400, detail="Unknown provider")

            body = await request.body()
            store = _load_store()

            if provider == "stripe":
                return await _stripe_webhook(config, body, request.headers, store)
            return await _airwallex_webhook(config, body, store)

        async def _stripe_webhook(config: dict, body: bytes, headers: dict, store: dict) -> dict:
            webhook_secret = config.get("stripe_webhook_secret")
            if config.get("stripe_secret_key") and webhook_secret:
                try:
                    import stripe
                except ImportError as exc:  # pragma: no cover
                    raise HTTPException(status_code=500, detail=f"stripe package not installed: {exc}")

                try:
                    sig = headers.get("stripe-signature", "")
                    event = stripe.Webhook.construct_event(body, sig, webhook_secret)
                    session = event.get("data", {}).get("object", {})
                    provider_tx_id = session.get("id")
                    order = next(
                        (o for o in store["orders"].values() if o.get("provider_tx_id") == provider_tx_id),
                        None,
                    )
                    if order and order["status"] != "paid":
                        order["status"] = "paid"
                        _credit_balance(store, order["user_id"], order["erc_amount"])
                        _save_store(store)
                        _ltm_sync_paid(order, store, config)
                    return {"received": True}
                except Exception as exc:
                    raise HTTPException(status_code=400, detail=f"Stripe webhook error: {exc}")

            return _simulate_webhook(store, "stripe")

        async def _airwallex_webhook(config: dict, body: bytes, store: dict) -> dict:
            if config.get("airwallex_api_key") and config.get("airwallex_client_id"):
                try:
                    data = json.loads(body)
                    order_id = data.get("merchant_order_id")
                    order = store["orders"].get(order_id)
                    if order and order["status"] != "paid":
                        order["status"] = "paid"
                        _credit_balance(store, order["user_id"], order["erc_amount"])
                        _save_store(store)
                        _ltm_sync_paid(order, store, config)
                    return {"received": True}
                except Exception as exc:
                    raise HTTPException(status_code=400, detail=f"Airwallex webhook error: {exc}")

            return _simulate_webhook(store, "airwallex")

        def _simulate_webhook(store: dict, provider: str) -> dict:
            pending = [o for o in store["orders"].values() if o["method"] == provider and o["status"] == "pending"]
            if pending:
                order = sorted(pending, key=lambda o: o["created_at"], reverse=True)[0]
                order["status"] = "paid"
                _credit_balance(store, order["user_id"], order["erc_amount"])
                _save_store(store)
                _ltm_sync_paid(order, store, config)
                return {"received": True, "order_id": order["order_id"], "status": "paid", "simulated": True}
            return {"received": True, "simulated": True}

        @router.post("/erc20/verify")
        async def erc20_verify(payload: ERC20VerifyPayload) -> dict:
            store = _load_store()
            order = store["orders"].get(payload.order_id)
            if not order:
                raise HTTPException(status_code=404, detail="Order not found")
            if order["method"] != "erc20":
                raise HTTPException(status_code=400, detail="Order is not ERC20")
            if order["status"] == "paid":
                return {
                    "success": True,
                    "order_id": order["order_id"],
                    "status": "paid",
                    "credits_added": 0,
                    "balance": store["balances"].get(order["user_id"], 0),
                }

            order["tx_hash"] = payload.tx_hash
            verified = await _verify_erc20_transfer(config, payload.tx_hash)

            if verified:
                order["status"] = "paid"
                _credit_balance(store, order["user_id"], order["erc_amount"])
                _save_store(store)
                _ltm_sync_paid(order, store, config)
                return {
                    "success": True,
                    "order_id": order["order_id"],
                    "status": "paid",
                    "credits_added": order["erc_amount"],
                    "balance": store["balances"].get(order["user_id"], 0),
                }

            raise HTTPException(status_code=400, detail="ERC20 transfer could not be verified")

        async def _verify_erc20_transfer(config: dict, tx_hash: str) -> bool:
            rpc_url = config.get("eth_rpc_url")
            contract_address = config.get("erc20_contract_address")
            merchant = config.get("merchant_wallet_address")
            if not rpc_url or not contract_address or not merchant:
                return True  # demo simulation

            try:
                async with httpx.AsyncClient() as client:
                    receipt_resp = await client.post(
                        rpc_url,
                        json={
                            "jsonrpc": "2.0",
                            "id": 1,
                            "method": "eth_getTransactionReceipt",
                            "params": [tx_hash],
                        },
                        timeout=30,
                    )
                    receipt_resp.raise_for_status()
                    receipt = receipt_resp.json().get("result")
                    if not receipt or int(receipt.get("status", "0x0"), 16) != 1:
                        return False

                    tx_resp = await client.post(
                        rpc_url,
                        json={
                            "jsonrpc": "2.0",
                            "id": 1,
                            "method": "eth_getTransactionByHash",
                            "params": [tx_hash],
                        },
                        timeout=30,
                    )
                    tx_resp.raise_for_status()
                    tx = tx_resp.json().get("result")
                    return bool(tx and tx.get("to", "").lower() == contract_address.lower())
            except Exception as exc:
                raise HTTPException(status_code=502, detail=f"RPC error: {exc}")

        @router.get("/wallet/{user_id}")
        async def wallet(user_id: str) -> dict:
            store = _load_store()
            orders = [o for o in store["orders"].values() if o.get("user_id") == user_id]
            return {
                "user_id": user_id,
                "erc_balance": store["balances"].get(user_id, 0),
                "orders": orders,
            }

        @router.post("/credits/use")
        async def use_credits(payload: UseCreditsPayload) -> dict:
            store = _load_store()
            balance = store["balances"].get(payload.user_id, 0)
            if balance < payload.amount:
                raise HTTPException(status_code=402, detail="Insufficient ERC balance")
            balance -= payload.amount
            store["balances"][payload.user_id] = balance
            tx_id = _new_tx_id()
            store["credit_txs"].append(
                {
                    "tx_id": tx_id,
                    "user_id": payload.user_id,
                    "amount": payload.amount,
                    "reason": payload.reason,
                    "created_at": time.time(),
                }
            )
            _save_store(store)
            _ltm_sync_wallet(
                payload.user_id,
                balance,
                {
                    "tx_id": tx_id,
                    "amount": -payload.amount,
                    "reason": payload.reason,
                },
                config,
            )
            return {
                "success": True,
                "remaining_balance": balance,
                "tx_id": tx_id,
            }

        app.include_router(router)

    def health(self) -> dict:
        config = self.context.get("payments_config", {})
        stripe_ok = bool(config.get("stripe_secret_key"))
        airwallex_ok = bool(config.get("airwallex_api_key") and config.get("airwallex_client_id"))
        erc_ok = bool(config.get("eth_rpc_url") and config.get("erc20_contract_address") and config.get("merchant_wallet_address"))
        return {
            "name": self.name,
            "version": self.version,
            "status": "ok",
            "stripe_configured": stripe_ok,
            "airwallex_configured": airwallex_ok,
            "erc20_configured": erc_ok,
            "ltm_url": config.get("ltm_url"),
            "ltm_sync": "enabled" if config.get("ltm_url") else "disabled",
            "mode": "live" if (stripe_ok or airwallex_ok or erc_ok) else "simulation",
        }
