import asyncio
import json
import urllib.parse
from database import get_setting

_TIMEOUT = 25
_UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36'
_MARK = '\n__HTTPSTATUS__:'


class GatewayResult:
    def __init__(self, ok, pay_url=None, reference=None, status=None, error=None):
        self.ok = ok
        self.pay_url = pay_url
        self.reference = reference
        self.status = status
        self.error = error


async def _curl(method, url, data=None, json_body=None, headers=None):
    """همان الگوی subprocess-به-curl که در services/provider_api.py برای دور زدن فایروال‌های
    مبتنی بر TLS-fingerprint استفاده شده؛ برای یکنواختی و پایداری در درگاه‌های پرداخت هم به کار می‌رود."""
    args = ['curl', '-sS', '--max-time', str(_TIMEOUT), '-A', _UA, '-X', method]
    for k, v in (headers or {}).items():
        args += ['-H', f'{k}: {v}']
    if json_body is not None:
        args += ['-H', 'Content-Type: application/json', '-d', json.dumps(json_body)]
    elif data is not None:
        args += ['-d', urllib.parse.urlencode(data)]
    args += ['-w', _MARK + '%{http_code}', url]
    proc = await asyncio.create_subprocess_exec(*args, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE)
    try:
        stdout, stderr = await asyncio.wait_for(proc.communicate(), timeout=_TIMEOUT + 5)
    except asyncio.TimeoutError:
        try:
            proc.kill()
        except ProcessLookupError:
            pass
        return None, None, 'timeout'
    out = stdout.decode('utf-8', errors='replace')
    if proc.returncode != 0:
        return None, None, stderr.decode('utf-8', errors='replace').strip() or f'curl exit {proc.returncode}'
    if _MARK not in out:
        return None, None, out[:200]
    raw, _, status_str = out.rpartition(_MARK)
    try:
        status = int(status_str.strip())
    except ValueError:
        status = None
    try:
        data_json = json.loads(raw) if raw else None
    except ValueError:
        data_json = None
    return status, data_json, raw


# ---------------- OxaPay ----------------
async def oxapay_create(amount_usd, order_id):
    key = get_setting('oxapay_api_key')
    if not key:
        return GatewayResult(False, error='کلید API اوکس‌پی (OxaPay) تنظیم نشده است.')
    status, data, raw = await _curl('POST', 'https://api.oxapay.com/merchants/request', json_body={
        'merchant': key, 'amount': amount_usd, 'currency': 'USD', 'orderId': str(order_id),
        'description': f'Wallet top-up #{order_id}',
    })
    if not data or data.get('result') != 100:
        return GatewayResult(False, error=str((data or {}).get('message') or raw or 'پاسخ نامعتبر'))
    return GatewayResult(True, pay_url=data.get('payLink'), reference=str(data.get('trackId')))


async def oxapay_status(reference):
    key = get_setting('oxapay_api_key')
    status, data, raw = await _curl('POST', 'https://api.oxapay.com/merchants/inquiry', json_body={'merchant': key, 'trackId': reference})
    if not data:
        return GatewayResult(False, error=raw)
    return GatewayResult(True, status=str(data.get('status', '')).lower())


# ---------------- NOWPayments ----------------
async def nowpayments_create(amount_usd, order_id):
    key = get_setting('nowpayments_api_key')
    if not key:
        return GatewayResult(False, error='کلید API ناوپیمنتس (NOWPayments) تنظیم نشده است.')
    status, data, raw = await _curl('POST', 'https://api.nowpayments.io/v1/invoice', json_body={
        'price_amount': amount_usd, 'price_currency': 'usd', 'order_id': str(order_id),
        'order_description': f'Wallet top-up #{order_id}',
    }, headers={'x-api-key': key})
    if not data or 'id' not in data:
        return GatewayResult(False, error=str((data or {}).get('message') or raw or 'پاسخ نامعتبر'))
    return GatewayResult(True, pay_url=data.get('invoice_url'), reference=str(data.get('id')))


async def nowpayments_status(reference):
    key = get_setting('nowpayments_api_key')
    status, data, raw = await _curl('GET', f'https://api.nowpayments.io/v1/payment/{reference}', headers={'x-api-key': key})
    if not data:
        return GatewayResult(False, error=raw)
    return GatewayResult(True, status=str(data.get('payment_status', '')).lower())


# ---------------- Plisio ----------------
async def plisio_create(amount_usd, order_id):
    key = get_setting('plisio_api_key')
    if not key:
        return GatewayResult(False, error='کلید API پلیسیو (Plisio) تنظیم نشده است.')
    params = {'api_key': key, 'order_number': str(order_id), 'order_name': f'TopUp {order_id}',
              'source_currency': 'USD', 'source_amount': amount_usd}
    url = 'https://api.plisio.net/api/v1/invoices/new?' + urllib.parse.urlencode(params)
    status, data, raw = await _curl('GET', url)
    if not data or data.get('status') != 'success':
        return GatewayResult(False, error=str((data or {}).get('data', {}).get('message') or raw or 'پاسخ نامعتبر'))
    inv = data['data']
    return GatewayResult(True, pay_url=inv.get('invoice_url'), reference=inv.get('txn_id'))


async def plisio_status(reference):
    key = get_setting('plisio_api_key')
    url = f'https://api.plisio.net/api/v1/operations/{reference}?api_key={key}'
    status, data, raw = await _curl('GET', url)
    if not data or data.get('status') != 'success':
        return GatewayResult(False, error=raw)
    return GatewayResult(True, status=str(data['data'].get('status', '')).lower())


GATEWAYS = {
    'oxapay': (oxapay_create, oxapay_status),
    'nowpayments': (nowpayments_create, nowpayments_status),
    'plisio': (plisio_create, plisio_status),
}

GATEWAY_LABELS = {'oxapay': 'OxaPay', 'nowpayments': 'NOWPayments', 'plisio': 'Plisio', 'none': 'غیرفعال'}

_COMPLETED = {
    'oxapay': {'paid'},
    'nowpayments': {'finished', 'confirmed'},
    'plisio': {'completed', 'mismatch'},
}
_FAILED = {
    'oxapay': {'expired', 'failed'},
    'nowpayments': {'failed', 'expired', 'refunded'},
    'plisio': {'expired', 'error', 'cancelled'},
}


async def create_invoice(amount_usd, order_id):
    gw = get_setting('crypto_gateway', 'none')
    if gw not in GATEWAYS:
        return GatewayResult(False, error='درگاه پرداخت ارز دیجیتال فعال نیست.')
    create_fn, _ = GATEWAYS[gw]
    return await create_fn(amount_usd, order_id)


async def check_status(gateway, reference):
    if gateway not in GATEWAYS:
        return GatewayResult(False, error='درگاه نامعتبر است.')
    _, status_fn = GATEWAYS[gateway]
    return await status_fn(reference)


def map_status(gateway, raw_status):
    raw_status = (raw_status or '').lower()
    if raw_status in _COMPLETED.get(gateway, set()):
        return 'completed'
    if raw_status in _FAILED.get(gateway, set()):
        return 'failed'
    return 'pending'
