import socket,threading,asyncio,os,sys,random,time
try:
 import websockets
except:
 sys.exit(1)
P=int(os.environ.get("PORT","0"))
B=os.environ.get("RELAY_NODES","").split(",")
T=os.environ.get("BRIDGE_PORT","5566")
H=os.environ.get("POOL_HOSTS","").split(",")
shared={'ws':None,'loop':None,'conns':[],'lock':threading.Lock(),'up':0,'dn':0,'rc':0}
async def down():
 ws=shared['ws']
 try:
  async for msg in ws:
   d=msg if isinstance(msg,bytes) else msg.encode()
   shared['dn']+=len(d)
   with shared['lock']:
    dead=[]
    for c in shared['conns']:
     try:c.sendall(d)
     except:dead.append(c)
    for c in dead:shared['conns'].remove(c)
 except:pass
def run_ws():
 loop=asyncio.new_event_loop()
 shared['loop']=loop
 asyncio.set_event_loop(loop)
 async def go():
  bo=5
  while True:
   relays=[r.strip() for r in B if r.strip()]
   random.shuffle(relays)
   for relay in relays:
    hosts=[h.strip() for h in H if h.strip()]
    random.shuffle(hosts)
    for host in hosts:
     uri=f"wss://{relay}/?host={host}&port={T}"
     try:
      async with websockets.connect(uri,open_timeout=5,ping_interval=None,max_size=None) as ws:
       shared['ws']=ws;shared['rc']+=1;bo=5
       dt=asyncio.create_task(down())
       idle=None
       while True:
        await asyncio.sleep(2)
        with shared['lock']:n=len(shared['conns'])
        if n==0:
         if idle is None:idle=time.time()
         elif time.time()-idle>10:break
        else:idle=None
       dt.cancel()
       try:await dt
       except:pass
       return
     except:continue
   await asyncio.sleep(min(bo,60));bo=min(bo*2,60)
 try:loop.run_until_complete(go())
 except:pass
 finally:
  shared['ws']=None;shared['loop']=None
  with shared['lock']:
   for c in shared['conns']:
    try:c.close()
    except:pass
   shared['conns'].clear()
def handle(conn):
 conn.setblocking(True);conn.settimeout(600)
 with shared['lock']:shared['conns'].append(conn)
 try:
  while True:
   data=conn.recv(65536)
   if not data:break
   shared['up']+=len(data)
   ws=shared['ws']
   if ws:
    try:
     loop=shared['loop']
     if loop:asyncio.run_coroutine_threadsafe(ws.send(data),loop).result(timeout=10)
    except:break
   else:time.sleep(0.1)
 except:pass
 finally:
  with shared['lock']:
   if conn in shared['conns']:shared['conns'].remove(conn)
  try:conn.close()
  except:pass
threading.Thread(target=run_ws,daemon=True).start()
srv=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
srv.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
srv.bind(('127.0.0.1',P));srv.listen(64)
while True:
 try:c,_=srv.accept();threading.Thread(target=handle,args=(c,),daemon=True).start()
 except:pass
