"""
Proxy transparente entre o client Karos (AMo.exe) e o servidor REAL (85.119.149.223:10001).
Deixa tudo passar sem modificar nada, so loga em hexdump os dois sentidos -- objetivo e capturar
a resposta de login de verdade do servidor pra comparar com o nosso login_server.py experimental.

Uso:
    python real_server_proxy.py [porta_local]

Depois aponta o ServerList.ten do client pra 127.0.0.1:<porta_local> (ver redirect_to_proxy.py).
"""

from __future__ import annotations

import socket
import sys
import threading
import datetime

REAL_HOST = "85.119.149.223"
REAL_PORT = 10001
LOCAL_HOST = "0.0.0.0"
DEFAULT_LOCAL_PORT = 20000


def ts():
    return datetime.datetime.now().strftime("%H:%M:%S.%f")[:-3]


def hexdump(data: bytes, max_len: int = 512) -> str:
    data = data[:max_len]
    lines = []
    for i in range(0, len(data), 16):
        chunk = data[i:i + 16]
        hexpart = " ".join(f"{b:02x}" for b in chunk)
        asciipart = "".join(chr(b) if 32 <= b < 127 else "." for b in chunk)
        lines.append(f"  {i:04x}  {hexpart:<48}  {asciipart}")
    return "\n".join(lines)


def pipe(src: socket.socket, dst: socket.socket, label: str):
    try:
        while True:
            data = src.recv(65536)
            if not data:
                break
            print(f"[{ts()}] [{label}] {len(data)} bytes:\n{hexdump(data)}", flush=True)
            dst.sendall(data)
    except Exception as e:
        print(f"[{ts()}] [{label}] encerrado: {e}", flush=True)
    finally:
        try:
            dst.shutdown(socket.SHUT_WR)
        except Exception:
            pass


def handle_client(client_conn: socket.socket, addr):
    print(f"[{ts()}] [+] client conectou de {addr}", flush=True)
    try:
        server_conn = socket.create_connection((REAL_HOST, REAL_PORT), timeout=10)
    except Exception as e:
        print(f"[{ts()}] [!] falha ao conectar no servidor real: {e}", flush=True)
        client_conn.close()
        return
    print(f"[{ts()}] [+] conectado no servidor real {REAL_HOST}:{REAL_PORT}", flush=True)

    t1 = threading.Thread(target=pipe, args=(client_conn, server_conn, "CLIENT->SERVER"), daemon=True)
    t2 = threading.Thread(target=pipe, args=(server_conn, client_conn, "SERVER->CLIENT"), daemon=True)
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    client_conn.close()
    server_conn.close()
    print(f"[{ts()}] [-] conexao {addr} encerrada", flush=True)


def main():
    port = int(sys.argv[1]) if len(sys.argv) > 1 else DEFAULT_LOCAL_PORT
    srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    srv.bind((LOCAL_HOST, port))
    srv.listen(5)
    print(f"proxy escutando em {LOCAL_HOST}:{port} -> {REAL_HOST}:{REAL_PORT}", flush=True)
    while True:
        conn, addr = srv.accept()
        threading.Thread(target=handle_client, args=(conn, addr), daemon=True).start()


if __name__ == "__main__":
    main()
