"""NOWPayments.io API client + IPN signature verification

Keys are read from MongoDB Settings (admin panel) first, then from environment
variables as a fallback. This makes deployments cPanel-safe — admins can rotate
keys via the Admin → Settings page without touching `.env`.
"""
import os
import hmac
import hashlib
import json
import logging
import httpx
from typing import Dict, Optional

from db import get_db

logger = logging.getLogger(__name__)

API_BASE = os.environ.get("NOWPAYMENTS_API_BASE", "https://api.nowpayments.io/v1")


async def _get_credentials() -> Dict[str, str]:
    """Return {api_key, ipn_secret, public_url} preferring DB settings over env."""
    api_key = ""
    ipn_secret = ""
    public_url = ""
    try:
        db = get_db()
        doc = await db.settings.find_one({"id": "global"}, {"_id": 0}) or {}
        api_key = (doc.get("nowpayments_api_key") or "").strip()
        ipn_secret = (doc.get("nowpayments_ipn_secret") or "").strip()
        public_url = (doc.get("nowpayments_public_url") or "").strip()
    except Exception as e:
        logger.warning("Could not read NOWPayments settings from DB: %s", e)
    # env fallback
    if not api_key:
        api_key = os.environ.get("NOWPAYMENTS_API_KEY", "").strip()
    if not ipn_secret:
        ipn_secret = os.environ.get("NOWPAYMENTS_IPN_SECRET", "").strip()
    if not public_url:
        public_url = os.environ.get("PUBLIC_URL", "").strip()
    return {"api_key": api_key, "ipn_secret": ipn_secret, "public_url": public_url}


async def _headers() -> Dict[str, str]:
    creds = await _get_credentials()
    return {"x-api-key": creds["api_key"], "Content-Type": "application/json"}


async def get_public_url() -> str:
    return (await _get_credentials())["public_url"]


async def create_payment(
    price_amount: float,
    pay_currency: str,
    order_id: str,
    order_description: str,
    ipn_callback_url: str,
) -> Dict:
    """Create a new payment with NOWPayments. Returns payment dict."""
    payload = {
        "price_amount": round(price_amount, 2),
        "price_currency": "usd",
        "pay_currency": pay_currency,
        "order_id": order_id,
        "order_description": order_description,
        "ipn_callback_url": ipn_callback_url,
    }
    headers = await _headers()
    async with httpx.AsyncClient(timeout=30.0) as client:
        r = await client.post(f"{API_BASE}/payment", headers=headers, json=payload)
        if r.status_code >= 400:
            logger.error("NOWPayments create_payment error %s: %s", r.status_code, r.text)
            raise RuntimeError(f"NOWPayments error: {r.text}")
        return r.json()


async def get_payment_status(payment_id: str) -> Dict:
    headers = await _headers()
    async with httpx.AsyncClient(timeout=20.0) as client:
        r = await client.get(f"{API_BASE}/payment/{payment_id}", headers=headers)
        if r.status_code >= 400:
            logger.error("NOWPayments status error %s: %s", r.status_code, r.text)
            raise RuntimeError(f"NOWPayments status error: {r.text}")
        return r.json()


async def get_api_status() -> bool:
    try:
        headers = await _headers()
        async with httpx.AsyncClient(timeout=10.0) as client:
            r = await client.get(f"{API_BASE}/status", headers=headers)
            return r.status_code == 200
    except Exception as e:
        logger.error("NOWPayments status check failed: %s", e)
        return False


async def verify_ipn_signature(body_text: str, received_signature: str) -> bool:
    """Verify HMAC-SHA512 signature of IPN webhook body.

    NOWPayments signs the JSON body sorted alphabetically by keys with
    the IPN secret as the key.
    """
    creds = await _get_credentials()
    ipn_secret = creds["ipn_secret"]
    if not received_signature or not ipn_secret:
        return False
    try:
        body_dict = json.loads(body_text)
        sorted_body = json.dumps(body_dict, separators=(",", ":"), sort_keys=True)
        expected = hmac.new(
            ipn_secret.encode("utf-8"),
            sorted_body.encode("utf-8"),
            hashlib.sha512,
        ).hexdigest()
        return hmac.compare_digest(expected, received_signature)
    except Exception as e:
        logger.error("IPN signature verification error: %s", e)
        return False


async def get_min_amount(pay_currency: str) -> Optional[float]:
    """Returns the minimum amount (in pay_currency, e.g. USDT) that NOWPayments will accept."""
    try:
        headers = await _headers()
        async with httpx.AsyncClient(timeout=15.0) as client:
            r = await client.get(
                f"{API_BASE}/min-amount",
                params={"currency_from": pay_currency, "currency_to": pay_currency},
                headers=headers,
            )
            if r.status_code >= 400:
                logger.warning("min-amount fetch failed %s: %s", r.status_code, r.text)
                return None
            data = r.json()
            return float(data.get("min_amount") or 0)
    except Exception as e:
        logger.warning("min-amount fetch exception: %s", e)
        return None


# Statuses we treat as "paid" terminal-ish:
PAID_STATUSES = {"finished", "confirmed", "sending"}
FAILED_STATUSES = {"failed", "expired", "refunded"}


def is_paid_status(status: Optional[str]) -> bool:
    return (status or "").lower() in PAID_STATUSES


def is_failed_status(status: Optional[str]) -> bool:
    return (status or "").lower() in FAILED_STATUSES
