from pathlib import Path
import re,sys
server=Path(sys.argv[1]);p=server/'relay_server_clean.py';s=p.read_text(encoding='utf-8')
s,n=re.subn(r'VERSION\s*=\s*["\'][^"\']+["\']','VERSION  = "2.4.9.396-professional-security-modes-tor-ready"',s,count=1)
if n!=1:raise SystemExit('VERSION_PATCH_COUNT='+str(n))
start='# BEGIN V249396_PROFESSIONAL_SECURITY_MODES_TOR_READY_ROUTES'
if start not in s:
    routes=r'''
# BEGIN V249396_PROFESSIONAL_SECURITY_MODES_TOR_READY_ROUTES
try:
    import communication_security_v249396 as _commsec_v249396
except Exception:
    _commsec_v249396 = None
    LOG.exception("V249396 communication security module import failed")

def _comm396_module():
    if _commsec_v249396 is None:
        raise HTTPException(503,"communication_security_v249396_unavailable")
    return _commsec_v249396

def _comm396_peer(channel: str, device_id: str) -> str:
    parts=[str(x or '').strip() for x in str(channel or '').split(':')]
    if len(parts)!=3 or parts[0]!='dm':raise HTTPException(400,'direct_channel_required')
    if str(device_id) not in parts[1:]:raise HTTPException(403,'device_not_part_of_direct_channel')
    return parts[2] if parts[1]==str(device_id) else parts[1]

def _comm396_person(room_id: str, device_id: str) -> dict:
    try:
        r=one("SELECT nickname,avatar,last_seen FROM people WHERE room_id=? AND device_id=? LIMIT 1",(room_id,device_id))
        if r:return {'device_id':device_id,'nickname':str(r['nickname'] or 'Μέλος'),'avatar':str(r['avatar'] or ''),'last_seen':float(r['last_seen'] or 0)}
    except Exception:pass
    return {'device_id':device_id,'nickname':'Μέλος','avatar':'','last_seen':0}

def _comm396_cleanup() -> int:
    m=_comm396_module();ids=m.expired_message_ids();deleted=0
    for mid in ids:
        try:
            cur=execute("DELETE FROM messages WHERE message_id=?",(mid,));deleted+=max(0,int(getattr(cur,'rowcount',0) or 0))
        except Exception:LOG.exception('V249396 cleanup failed for %s',mid)
    m.delete_meta(ids);return deleted

@app.get('/api/communication/security_mode_v249396')
def api_communication_security_mode_get_v249396(room_id: str=Query('ithaki-phone-test'),channel: str=Query(...),viewer_id: str=Query(...)):
    m=_comm396_module();peer=_comm396_peer(channel,viewer_id)
    if _is_user_blocked_between(room_id,viewer_id,peer):raise HTTPException(403,'blocked_by_member')
    if not _are_friends_v249227(room_id,viewer_id,peer):raise HTTPException(status_code=403,detail={'ok':False,'reason':'friend_required'})
    return m.get_mode(room_id,channel)

@app.post('/api/communication/security_mode_v249396')
def api_communication_security_mode_save_v249396(payload: dict):
    m=_comm396_module();room_id=str(payload.get('room_id') or 'ithaki-phone-test')[:120];channel=str(payload.get('channel') or '')[:400];device_id=str(payload.get('device_id') or '')[:180];peer=_comm396_peer(channel,device_id)
    security_member_guard(room_id,device_id,'conversation_security_mode',max_per_minute=10,max_per_hour=60)
    if _is_user_blocked_between(room_id,device_id,peer):raise HTTPException(403,'blocked_by_member')
    if not _are_friends_v249227(room_id,device_id,peer):raise HTTPException(status_code=403,detail={'ok':False,'reason':'friend_required','message':'Πρώτα χρειάζεται αποδοχή φιλίας.'})
    try:out=m.save_mode(room_id,channel,device_id,str(payload.get('mode') or ''),payload.get('plaintext_ack') is True)
    except m.CommunicationSecurityError as exc:raise HTTPException(400,str(exc))
    try:ws_notify(room_id)
    except Exception:pass
    return out

@app.post('/api/communication/messages/send_v249396')
def api_communication_messages_send_v249396(payload: dict):
    m=_comm396_module();room_id=str(payload.get('room_id') or 'ithaki-phone-test')[:120];channel=str(payload.get('channel') or '')[:400];sender_id=str(payload.get('sender_id') or '')[:180];recipient_id=str(payload.get('recipient_id') or '')[:180];nickname=str(payload.get('nickname') or 'Μέλος')[:80];peer=_comm396_peer(channel,sender_id)
    if peer!=recipient_id:raise HTTPException(400,'recipient_channel_mismatch')
    security_member_guard(room_id,sender_id,'communication_message_v249396',max_per_minute=12,max_per_hour=80)
    if is_banned(room_id,sender_id):raise HTTPException(403,'member_blocked')
    if _is_user_blocked_between(room_id,sender_id,recipient_id):raise HTTPException(403,'blocked_by_member')
    if not _are_friends_v249227(room_id,sender_id,recipient_id):raise HTTPException(status_code=403,detail={'ok':False,'reason':'friend_required','message':'Πρώτα χρειάζεται αποδοχή φιλίας.'})
    stored=m.get_mode(room_id,channel);mode=m.normalize_mode(payload.get('security_mode'))
    if stored.get('updated_at',0)>0 and mode!=stored.get('mode'):raise HTTPException(status_code=409,detail={'ok':False,'reason':'security_mode_changed','message':'Ο τρόπος προστασίας άλλαξε. Άνοιξε ξανά την κλειδαριά.'})
    if mode=='full_e2ee':raise HTTPException(400,'full_e2ee_uses_secure_v249389_endpoint')
    is_encrypted=bool(payload.get('is_encrypted'));text=str(payload.get('text') or '');iv=str(payload.get('iv') or '');cipher=str(payload.get('cipher_text') or '')
    if mode=='standard_aes':
        if not is_encrypted or not iv or not cipher:raise HTTPException(400,'standard_aes_requires_ciphertext')
        text=''
    else:
        if payload.get('plaintext_ack') is not True:raise HTTPException(400,'plaintext_ack_required')
        if is_encrypted:raise HTTPException(400,'no_encryption_must_be_plaintext')
        text=text.strip()
        if not text:raise HTTPException(400,'empty_message')
        if len(text)>2500000:raise HTTPException(413,'message_too_large')
    ttl=m.normalize_ttl(payload.get('auto_delete_seconds'));storage=m.normalize_storage(payload.get('storage_mode'));message_type=str(payload.get('message_type') or 'message')[:40]
    if message_type not in ('message','photo','video','audio','file'):message_type='message'
    mid=nid('msg');created=now();item={'message_id':mid,'room_id':room_id,'channel':channel,'sender_id':sender_id,'nickname':nickname,'language':'el','text':text,'is_encrypted':is_encrypted,'iv':iv,'cipher_text':cipher,'created_at':created,'recipient_id':recipient_id,'security_mode':mode,'security_meta':{'mode':mode,'message_type':message_type,'auto_delete_seconds':ttl,'storage_mode':storage,'read_at':0,'expires_at':0}}
    if storage=='zero_online_only':
        person=_comm396_person(room_id,recipient_id)
        if float(person.get('last_seen') or 0)<created-45:raise HTTPException(status_code=409,detail={'ok':False,'reason':'recipient_offline_zero_storage','message':'Η μηδενική αποθήκευση λειτουργεί μόνο όταν ο φίλος είναι online.'})
        m.queue_zero(item)
        try:ws_notify(room_id)
        except Exception:pass
        return {'ok':True,'version':m.VERSION,'message_id':mid,'security_mode':mode,'server_persisted':False,'fcm_content_included':False}
    execute("INSERT INTO messages(message_id,room_id,channel,sender_id,nickname,language,text,is_encrypted,iv,cipher_text,created_at) VALUES(?,?,?,?,?,?,?,?,?,?,?)",(mid,room_id,channel,sender_id,nickname,'el',text,1 if is_encrypted else 0,iv,cipher,created))
    event_id,nonce=m.new_wake(room_id,channel,sender_id,recipient_id,mode,message_type);m.record_message(mid,room_id,channel,sender_id,recipient_id,mode,message_type,ttl,storage,'')
    push=push_notify_devices(room_id,[recipient_id],'OK ITHAKI','',{'kind':'communication_message_wake','event_id':event_id,'nonce':nonce,'room_id':room_id})
    try:ws_notify(room_id)
    except Exception:pass
    return {'ok':True,'version':m.VERSION,'message_id':mid,'security_mode':mode,'server_persisted':True,'push':push,'fcm_content_included':False}

@app.get('/api/communication/messages_v249396')
def api_communication_messages_get_v249396(room_id: str=Query('ithaki-phone-test'),channel: str=Query(...),viewer_id: str=Query(...),since: float=Query(0),limit: int=Query(300,ge=1,le=500)):
    m=_comm396_module();peer=_comm396_peer(channel,viewer_id)
    if _is_user_blocked_between(room_id,viewer_id,peer):raise HTTPException(403,'blocked_by_member')
    if not _are_friends_v249227(room_id,viewer_id,peer):raise HTTPException(status_code=403,detail={'ok':False,'reason':'friend_required'})
    _comm396_cleanup()
    try:_secure_cleanup_v249389()
    except Exception:pass
    result=[]
    for r in rows("SELECT * FROM messages WHERE room_id=? AND channel=? AND created_at>? ORDER BY created_at ASC LIMIT ?",(room_id,channel,since,limit)):
        d=dict(r);d['is_encrypted']=bool(d.get('is_encrypted'));p=_comm396_person(room_id,str(d.get('sender_id') or ''));d['avatar']=p.get('avatar') or '';d['photo']=d['avatar'];result.append(d)
    zero=[]
    try:zero.extend(_secure_v249389.pop_zero_messages(room_id,channel,viewer_id))
    except Exception:pass
    zero.extend(m.pop_zero(room_id,channel,viewer_id));result.extend(zero);result.sort(key=lambda x:float(x.get('created_at') or 0))
    ids=[str(x.get('message_id') or '') for x in result];commmeta=m.meta_for_messages(ids)
    try:securemeta=_secure_v249389.meta_for_messages(ids)
    except Exception:securemeta={}
    plaintext=False
    for item in result:
        mid=str(item.get('message_id') or '')
        if str(item.get('iv') or '')=='v249389:rsa-oaep-aes-gcm':mode='full_e2ee'
        elif bool(item.get('is_encrypted')):mode='standard_aes'
        else:mode='no_encryption';plaintext=True
        item['security_mode']=mode
        if mid in commmeta:item['security_meta']=commmeta[mid]
        elif mid in securemeta:item['secure_meta']=securemeta[mid]
    return {'ok':True,'version':m.VERSION,'messages':result[-limit:],'zero_storage_delivered':len(zero),'server_plaintext_content_present':plaintext}

@app.post('/api/communication/messages/read_v249396')
def api_communication_messages_read_v249396(payload: dict):
    m=_comm396_module();room_id=str(payload.get('room_id') or 'ithaki-phone-test')[:120];channel=str(payload.get('channel') or '')[:400];mid=str(payload.get('message_id') or '')[:180];reader=str(payload.get('reader_id') or '')[:180];_comm396_peer(channel,reader)
    row=one("SELECT iv FROM messages WHERE room_id=? AND channel=? AND message_id=? LIMIT 1",(room_id,channel,mid))
    try:
        if row and str(row['iv'] or '')=='v249389:rsa-oaep-aes-gcm':out=_secure_v249389.mark_read(mid,reader)
        else:out=m.mark_read(mid,reader)
    except Exception as exc:raise HTTPException(403,str(exc))
    _comm396_cleanup()
    try:_secure_cleanup_v249389()
    except Exception:pass
    try:ws_notify(room_id)
    except Exception:pass
    return {'version':m.VERSION,**out}

@app.get('/api/communication/wake_metadata_v249396')
def api_communication_wake_metadata_v249396(room_id: str=Query('ithaki-phone-test'),recipient_id: str=Query(...),event_id: str=Query(...),nonce: str=Query(...)):
    m=_comm396_module();item=m.verify_wake(event_id,recipient_id,nonce)
    if not item:raise HTTPException(404,'wake_not_found')
    sender=str(item.get('sender_id') or '')
    if not _are_friends_v249227(room_id,sender,recipient_id):raise HTTPException(403,'friendship_no_longer_accepted')
    p=_comm396_person(room_id,sender)
    return {'ok':True,'version':m.VERSION,'event_kind':'message','sender_id':sender,'sender_name':p.get('nickname') or 'Μέλος','sender_avatar':p.get('avatar') or '','channel':str(item.get('channel') or ''),'message_type':str(item.get('message_type') or 'message'),'security_mode':str(item.get('mode') or 'full_e2ee'),'friend_accepted':True}

@app.get('/api/diagnostics/communication_security_v249396')
def api_diagnostics_communication_security_v249396():
    return _comm396_module().diagnostics()
# END V249396_PROFESSIONAL_SECURITY_MODES_TOR_READY_ROUTES
'''
    s += '\n' + routes + '\n'
p.write_text(s,encoding='utf-8')
print('SERVER_VERSION_V249396=OK')
print('COMMUNICATION_SECURITY_ROUTES_V249396=OK')
