"""Standard-library signing and webhook verification. No network requests."""
import hashlib
import hmac
import json
import secrets
import time
from urllib.parse import quote, unquote, unquote_plus

def canonical_target(target):
    if not target.startswith('/') or target.startswith('//') or any(ord(c) <= 32 or c in '#\\' for c in target):
        raise ValueError('invalid request target')
    parts = target.split('?')
    if len(parts) > 2 or '//' in parts[0]:
        raise ValueError('invalid request target')
    import re
    if re.search(r'%(?![0-9a-fA-F]{2})', target):
        raise ValueError('invalid percent encoding')
    segments = [unquote(s, errors='strict') for s in parts[0].split('/')]
    if any(s in ('.', '..') or any(ord(c) <= 32 or c in '/\\' for c in s) for s in segments):
        raise ValueError('invalid path')
    path = '/'.join(quote(s, safe='-._~') for s in segments)
    pairs = []
    if len(parts) == 2 and parts[1]:
        for pair in parts[1].split('&'):
            k, _, v = pair.partition('=')
            pairs.append((quote(unquote_plus(k, errors='strict'), safe='-._~'), quote(unquote_plus(v, errors='strict'), safe='-._~')))
    return path + ('?' + '&'.join(k + '=' + v for k, v in sorted(pairs)) if pairs else '')

def sign(secret, method, target, timestamp, nonce, raw_body):
    canonical = '\n'.join([method.upper(), canonical_target(target), str(timestamp), nonce, hashlib.sha256(raw_body).hexdigest()])
    return hmac.new(secret.encode(), canonical.encode(), hashlib.sha256).hexdigest()

def headers(secret, api_key, method, target, raw_body):
    timestamp, nonce = str(int(time.time())), secrets.token_hex(16)
    return {'Content-Type': 'application/json', 'x-api-key': api_key, 'X-PayAGL-Timestamp': timestamp,
            'X-PayAGL-Nonce': nonce, 'X-PayAGL-Signature': sign(secret, method, target, timestamp, nonce, raw_body)}

def verify_webhook(secret, raw_body, headers, now=None):
    """Returns authenticated delivery ID. Atomically deduplicate it in durable storage."""
    import re
    timestamp, delivery = headers.get('x-payagl-timestamp', ''), headers.get('x-payagl-delivery-id', '')
    if headers.get('x-payagl-signature-version') != 'v1' or not re.fullmatch(r'\d{10}', timestamp) or not re.fullmatch(r'[A-Za-z0-9_-]{16,128}', delivery):
        raise ValueError('invalid webhook headers')
    if abs((time.time() if now is None else now) - int(timestamp)) > 300:
        raise ValueError('expired webhook')
    expected = hmac.new(secret.encode(), (timestamp + '\n' + delivery + '\n').encode() + raw_body, hashlib.sha256).hexdigest()
    if not hmac.compare_digest(expected, headers.get('x-payagl-signature', '')):
        raise ValueError('invalid webhook signature')
    return delivery

if __name__ == '__main__':
    for target, body in [('/api/payment/create', {'amount':500,'order_id':'synthetic-payin'}),('/api/payout/create',{'amount':500,'order_id':'synthetic-payout','beneficiary_name':'Synthetic','account_number':'12345678','ifsc':'HDFC0000001'})]:
        raw = json.dumps(body, separators=(',', ':')).encode()
        print(json.dumps({'target':target,'body':raw.decode(),'headers':headers('synthetic-request-secret','synthetic-api-key','POST',target,raw)}))
