"""
Probe Surebet247 for BTTS by injecting custom RPC calls into the live browser WS session.

Approach:
  Step 1 - navigate normally and capture event IDs (no init script).
  Step 2 - navigate to a blank page so the WS closes cleanly.
  Step 3 - inject a WS-intercepting init script on the CONTEXT, then navigate again.
  Step 4 - send custom profile probes via page.evaluate(), capture replies.

Usage:
    python3 tools/discover_surebet247_btts_profiles.py
"""
import sys, os
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))

import time, collections
import msgpack
from scrapers.surebet247 import _Surebet247Worker, _decode_frame

BASE_SITE = 'https://www.surebet247.com'
CONTEXT   = ['en', 'MOBILE_WEB', 'CL38B1', '', 'NGN']

PROFILES_TO_TRY = [
    'pro_all',
    'pro_extended',
    'pro_popular',
    'pro_full',
    'pro_combo',
    'pro_dc',
    'pro_default',
    'pro_gg',
    'pro_btts',
    'pro_goals',
    'pro_specials',
    'pro_1x2_dc',
]

KNOWN_TYPES = {
    1: 'Home/Away',
    2: '1X2',
    4: 'Asian Handicap',
    5: 'Over/Under',
}


def encode_rpc(req_id: int, method: str, args: list) -> bytes:
    raw = msgpack.packb([4, {}, req_id, method, args], use_bin_type=True)
    return b'\x00' + raw


def probe_profiles(page) -> dict:
    # ── Step 1: navigate WITHOUT init script, capture event IDs ──────────────
    print('  Step 1: Loading football page normally...')
    frames_recv_1 = []
    frames_sent_1 = []
    last_recv = [time.time()]

    def handle_ws_1(ws):
        if 'direct-feed' not in ws.url:
            return
        ws.on('framereceived', lambda p: (frames_recv_1.append(p), last_recv.__setitem__(0, time.time())) if isinstance(p, bytes) else None)
        ws.on('framesent',     lambda p: frames_sent_1.append(p) if isinstance(p, bytes) else None)

    page.on('websocket', handle_ws_1)
    page.goto(f'{BASE_SITE}/sport/football', wait_until='domcontentloaded', timeout=60_000)
    time.sleep(8)
    for _ in range(5):
        page.mouse.wheel(0, 700)
        time.sleep(0.6)
    deadline = time.time() + 20
    while time.time() < deadline:
        time.sleep(1)
        if time.time() - last_recv[0] > 3.0 and frames_recv_1:
            break
    try:
        page.remove_listener('websocket', handle_ws_1)
    except Exception:
        pass

    print(f'  Step 1 done: {len(frames_recv_1)} recv / {len(frames_sent_1)} sent')

    # Extract event IDs
    event_ids = []
    for frame in frames_sent_1:
        if not isinstance(frame, bytes):
            continue
        d = _decode_frame(frame)
        if not d or not isinstance(d, (list, tuple)) or len(d) < 5:
            continue
        if str(d[3]) == 'GetMainMarketsByProfileAndEventIds':
            ids = d[4][1] if isinstance(d[4], (list, tuple)) and len(d[4]) > 1 else []
            if isinstance(ids, (list, tuple)):
                event_ids.extend(str(i) for i in ids)
    event_ids = list(dict.fromkeys(event_ids))[:6]

    if not event_ids:
        method_by_id = {}
        for frame in frames_sent_1:
            if isinstance(frame, bytes):
                d = _decode_frame(frame)
                if d and isinstance(d, (list, tuple)) and len(d) >= 4 and d[0] == 4:
                    method_by_id[str(d[2])] = str(d[3])
        rich_ids = {rid for rid, m in method_by_id.items()
                    if m in ('GetRichEventsByTournamentIdAndStage', 'GetRichEventsByIds')}
        for frame in frames_recv_1:
            if not isinstance(frame, bytes):
                continue
            d = _decode_frame(frame)
            if not d or not isinstance(d, (list, tuple)) or d[0] != 2 or len(d) < 4:
                continue
            if str(d[2]) not in rich_ids:
                continue
            reply = d[3]
            if not isinstance(reply, (list, tuple)) or len(reply) < 2 or not reply[0]:
                continue
            for item in (reply[1] or []):
                if isinstance(item, (list, tuple)) and len(item) >= 2:
                    eid = item[1]
                    if isinstance(eid, (int, str)):
                        event_ids.append(str(eid))
        event_ids = list(dict.fromkeys(event_ids))[:6]

    print(f'  Event IDs: {event_ids}')
    if not event_ids:
        print('  No event IDs — aborting.')
        return {}

    # ── Step 2: inject WS hook via context.add_init_script, then navigate again
    print('\n  Step 2: Adding WS hook via context, re-navigating...')
    try:
        ctx = page.context
        ctx.add_init_script("""
        (function() {
            var _orig = WebSocket.prototype.send;
            WebSocket.prototype.send = function(data) {
                if (!window._dfw && this.url && this.url.indexOf('direct-feed') !== -1) {
                    window._dfw = this;
                }
                return _orig.apply(this, arguments);
            };
        })();
        """)
        print('  Context init script added.')
    except Exception as e:
        print(f'  Context init script error: {e}')

    frames_recv_2 = []
    frames_sent_2 = []
    last_recv2 = [time.time()]

    def handle_ws_2(ws):
        if 'direct-feed' not in ws.url:
            return
        ws.on('framereceived', lambda p: (frames_recv_2.append(p), last_recv2.__setitem__(0, time.time())) if isinstance(p, bytes) else None)
        ws.on('framesent',     lambda p: frames_sent_2.append(p) if isinstance(p, bytes) else None)

    page.on('websocket', handle_ws_2)
    page.goto(f'{BASE_SITE}/sport/football', wait_until='domcontentloaded', timeout=60_000)
    time.sleep(10)
    for _ in range(3):
        page.mouse.wheel(0, 700)
        time.sleep(0.5)
    deadline = time.time() + 15
    while time.time() < deadline:
        time.sleep(1)
        if time.time() - last_recv2[0] > 3.0 and frames_recv_2:
            break

    print(f'  Step 2 load: {len(frames_recv_2)} recv / {len(frames_sent_2)} sent')

    ws_state = page.evaluate("""() => {
        if (!window._dfw) return 'not captured';
        return 'readyState=' + window._dfw.readyState;
    }""")
    print(f'  WS state in JS: {ws_state}')

    if 'readyState=1' not in ws_state:
        print('  WS not open — cannot inject probes.')
        return {}

    # ── Step 3: send probe frames ─────────────────────────────────────────────
    print('\n  Step 3: Sending profile probes...')
    PROBE_BASE = 9000
    pending = {}

    for i, profile in enumerate(PROFILES_TO_TRY):
        rid = PROBE_BASE + i
        pending[rid] = profile
        frame = encode_rpc(rid, 'GetMainMarketsByProfileAndEventIds',
                           [profile, event_ids, 4, 1, CONTEXT])
        result = page.evaluate("""(d) => {
            if (!window._dfw || window._dfw.readyState !== 1) return 'ws_closed';
            window._dfw.send(new Uint8Array(d).buffer);
            return 'ok';
        }""", list(frame))
        print(f'    {profile!r:25s} → {result}')
        time.sleep(0.4)

    time.sleep(7)

    # ── Step 4: parse replies ─────────────────────────────────────────────────
    results = {}
    all_recv = frames_recv_1 + frames_recv_2
    for frame in all_recv:
        if not isinstance(frame, bytes):
            continue
        d = _decode_frame(frame)
        if not d or not isinstance(d, (list, tuple)) or d[0] != 2 or len(d) < 4:
            continue
        rid = d[2]
        if rid not in pending:
            continue
        reply = d[3]
        if not isinstance(reply, (list, tuple)) or len(reply) < 2 or not reply[0]:
            results[rid] = {}
            continue
        items = reply[1]
        if not isinstance(items, (list, tuple)):
            results[rid] = {}
            continue
        market_types = collections.defaultdict(list)
        for item in items:
            if not isinstance(item, (list, tuple)) or len(item) < 3:
                continue
            item_key = item[1]
            if not isinstance(item_key, (list, tuple)) or len(item_key) < 3:
                continue
            mtype  = item_key[2]
            period = item_key[3] if len(item_key) > 3 else None
            mval   = item[2]
            if not isinstance(mval, (list, tuple)) or not mval:
                continue
            for group in (mval[0] or []):
                if not isinstance(group, (list, tuple)) or len(group) < 2:
                    continue
                spec = group[0]
                ocs  = []
                for oc in (group[1] or []):
                    if isinstance(oc, (list, tuple)) and len(oc) >= 2:
                        oc_type = oc[0][0] if isinstance(oc[0], (list, tuple)) and oc[0] else '?'
                        price   = round(oc[1] / 100, 2) if isinstance(oc[1], (int, float)) else oc[1]
                        ocs.append((oc_type, price))
                if ocs:
                    market_types[(mtype, period, str(spec))].append(ocs)
        results[rid] = dict(market_types)

    return {'pending': pending, 'results': results}


if __name__ == '__main__':
    print('Probing Surebet247 profiles for BTTS...\n')
    worker = _Surebet247Worker()
    data = worker.call(probe_profiles, timeout=250)

    if not data:
        print('No data returned.')
    else:
        pending = data.get('pending', {})
        results = data.get('results', {})

        print('\n\n═══ RESULTS ═══\n')
        for rid, profile in sorted(pending.items()):
            mkt = results.get(rid)
            if mkt is None:
                print(f'  {profile!r:25s} → no reply')
            elif not mkt:
                print(f'  {profile!r:25s} → empty / error')
            else:
                type_ids = sorted(set(t for (t, p, s) in mkt.keys()))
                new_types = [t for t in type_ids if t not in KNOWN_TYPES]
                label = ', '.join(KNOWN_TYPES.get(t, f'UNKNOWN({t})') for t in type_ids)
                flag  = '  ← NEW TYPES!' if new_types else ''
                print(f'  {profile!r:25s} → [{label}]{flag}')
                for (mtype, period, spec), samples in sorted(mkt.items()):
                    s = samples[0]
                    oc_ids = [o[0] for o in s]
                    prices = [o[1] for o in s]
                    known  = KNOWN_TYPES.get(mtype, f'UNKNOWN({mtype})')
                    print(f'    type={mtype:<5} period={period}  spec={spec:<15} {known:<20} oc_ids={oc_ids}  prices={prices}')
