import subprocess, json, hmac, hashlib, urllib.request, urllib.error

APP_SECRET = 'testappsecret'
VERIFY_TOKEN = 'testverifytoken'
URL_SECRET = 'testsecret123'
BASE = 'http://127.0.0.1:8102/api/whatsapp/webhook.php'

def sh(c): return subprocess.run(c, shell=True, capture_output=True, text=True).stdout.strip()
def sql(q): return sh("mysql -u root zc_safeguarding -N -e \"%s\"" % q)

def raw_request(method, url, body=b'', headers=None):
    req = urllib.request.Request(url, data=body if method == 'POST' else None, method=method, headers=headers or {})
    try:
        r = urllib.request.urlopen(req)
        return r.status, r.read().decode()
    except urllib.error.HTTPError as e:
        return e.code, e.read().decode()

def signed_post(payload_dict, secret=APP_SECRET, url_secret=URL_SECRET):
    body = json.dumps(payload_dict).encode()
    headers = {'Content-Type': 'application/json'}
    if secret is not None:
        sig = 'sha256=' + hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
        headers['X-Hub-Signature-256'] = sig
    url = f'{BASE}?s={url_secret}' if url_secret is not None else BASE
    return raw_request('POST', url, body, headers)

def message_payload(number, text, msg_id='wamid.TEST1', phone_number_id='999888777'):
    return {
        "object": "whatsapp_business_account",
        "entry": [{"id": "1", "changes": [{"value": {
            "messaging_product": "whatsapp",
            "metadata": {"phone_number_id": phone_number_id},
            "contacts": [{"wa_id": number}],
            "messages": [{"from": number, "id": msg_id, "timestamp": "1234567890", "type": "text", "text": {"body": text}}]
        }, "field": "messages"}]}]
    }

def status_payload(phone_number_id='999888777'):
    return {"object": "whatsapp_business_account", "entry": [{"id": "1", "changes": [{"value": {
        "messaging_product": "whatsapp", "metadata": {"phone_number_id": phone_number_id},
        "statuses": [{"id": "wamid.X", "status": "delivered"}]
    }, "field": "messages"}]}]}

ok = fail = 0
def check(name, cond, detail=''):
    global ok, fail
    if cond: ok += 1; print('PASS', name)
    else: fail += 1; print('FAIL', name, detail)

print('--- verification handshake (GET) ---')
st, body = raw_request('GET', f'{BASE}?s={URL_SECRET}&hub.mode=subscribe&hub.verify_token={VERIFY_TOKEN}&hub.challenge=abc123')
check('correct verify token echoes challenge', st == 200 and body == 'abc123', (st, body))
st, body = raw_request('GET', f'{BASE}?s={URL_SECRET}&hub.mode=subscribe&hub.verify_token=WRONG&hub.challenge=abc123')
check('wrong verify token rejected 404', st == 404, (st, body))
st, body = raw_request('GET', f'{BASE}&hub.mode=subscribe&hub.verify_token={VERIFY_TOKEN}&hub.challenge=abc123')
check('missing URL secret rejected even with valid verify token', st == 404, (st, body))
st, body = raw_request('GET', f'{BASE}?s={URL_SECRET}')
check('GET with no hub.mode rejected', st == 404, (st, body))

print('--- POST signature verification ---')
st, body = signed_post(message_payload('263771111222', 'hello'), secret=APP_SECRET)
check('valid signature + valid payload accepted', st == 200, (st, body))
st, body = signed_post(message_payload('263771111222', 'hello2', msg_id='wamid.BADSIG'), secret='wrongsecret')
check('wrong signature rejected 404', st == 404, (st, body))
st, body = signed_post(message_payload('263771111222', 'hello3', msg_id='wamid.NOSIG'), secret=None)
check('missing signature header rejected 404', st == 404, (st, body))
st, body = signed_post(message_payload('263771111222', 'hello4'), url_secret='wrong')
check('wrong URL secret rejected 404 even with valid Meta signature', st == 404, (st, body))

print('--- payload semantics ---')
before = sql('SELECT COUNT(*) FROM whatsapp_sessions')
st, body = signed_post(status_payload(), secret=APP_SECRET)
check('delivery-status callback (no messages[]) ignored, 200 OK', st == 200 and sql('SELECT COUNT(*) FROM whatsapp_sessions') == before, (st, body))
st, body = signed_post(message_payload('263771111222', 'hi', msg_id='wamid.WRONGPHONE', phone_number_id='000000'), secret=APP_SECRET)
check('mismatched phone_number_id ignored', st == 200 and sql('SELECT COUNT(*) FROM whatsapp_sessions') == before, (st, body))

print('--- idempotency across providers-shared code path ---')
n1 = sql('SELECT COUNT(*) FROM whatsapp_processed_events')
signed_post(message_payload('263773334444', 'menu', msg_id='wamid.DUPTEST'), secret=APP_SECRET)
n2 = sql('SELECT COUNT(*) FROM whatsapp_processed_events')
signed_post(message_payload('263773334444', 'menu', msg_id='wamid.DUPTEST'), secret=APP_SECRET)
n3 = sql('SELECT COUNT(*) FROM whatsapp_processed_events')
check('replayed wamid not double-processed', int(n2) == int(n1) + 1 and n3 == n2, (n1, n2, n3))
dump = sh('mysqldump -u root zc_safeguarding 2>/dev/null')
check('raw wamid not stored anywhere (message ids are hashed)', 'wamid.DUPTEST' not in dump)

print('--- full conversation over Cloud API transport ---')
num = '263779990000'
def talk(text, mid_suffix):
    return signed_post(message_payload(num, text, msg_id=f'wamid.CONV{mid_suffix}'), secret=APP_SECRET)
talk('hi', 1); talk('1', 2); talk('2', 3); talk('2', 4); talk('6', 5)
talk('me', 6); talk('now', 7); talk('1', 8); talk('field', 9)
talk('Something happened via the official platform.', 10)
st, body = talk('DONE', 11)
check('reaches confirm step over Cloud API', st == 200, body)
check('outbox log used cloud_api provider tag', 'cloud_api' in open('/home/claude/zc/zc/storage/logs/whatsapp-outbox.log').read())

print(f'\nRESULT: {ok} passed, {fail} failed')
