import socket
import threading
import sys
import os
import shutil

try:
    from colorama import init, Fore, Back, Style
    init(autoreset=True)
    HAS_COLOR = True
except ImportError:
    HAS_COLOR = False

SERVER_HOST = "mainline.proxy.rlwy.net" 
SERVER_PORT = 45597              # <-- Replace with your Railway port

# ── Globals ───────────────────────────────────────────────────────────────────
my_name      = ""
partner_name = "Partner"
chat_active  = False
print_lock   = threading.Lock()
stop_event   = threading.Event()

# ── Helpers ───────────────────────────────────────────────────────────────────

def W():
    return shutil.get_terminal_size((80, 24)).columns

def col(text, *codes):
    if not HAS_COLOR:
        return str(text)
    return "".join(codes) + str(text) + Style.RESET_ALL

def clear_screen():
    os.system("cls" if os.name == "nt" else "clear")

# ── UI printers ───────────────────────────────────────────────────────────────

def print_mine(msg):
    """My message — LEFT side, green."""
    bubble = col(f"  {msg}  ", Fore.WHITE, Back.GREEN)
    with print_lock:
        sys.stdout.write(f"\r\033[K{bubble}\n\n")
        sys.stdout.flush()

def print_theirs(name, msg):
    """Their message — RIGHT side, blue."""
    w        = W()
    name_tag = col(name, Fore.CYAN, Style.BRIGHT)
    bubble   = col(f"  {msg}  ", Fore.WHITE, Back.BLUE)
    vis_len  = len(name) + 1  # name tag line
    bub_len  = len(msg) + 4   # bubble line
    pad_name = max(w - vis_len - 1, 0)
    pad_bub  = max(w - bub_len - 1, 0)
    with print_lock:
        sys.stdout.write(f"\r\033[K{' ' * pad_name}{name_tag}\n")
        sys.stdout.write(f"\r\033[K{' ' * pad_bub}{bubble}\n\n")
        sys.stdout.flush()

def print_system(msg):
    """Centered yellow notice."""
    w   = W()
    pad = max((w - len(msg)) // 2, 0)
    with print_lock:
        sys.stdout.write(f"\r\033[K{' ' * pad}{col(msg, Fore.YELLOW)}\n")
        sys.stdout.flush()

def show_prompt():
    with print_lock:
        sys.stdout.write(col(f"  ✏  {my_name}: ", Fore.GREEN, Style.BRIGHT))
        sys.stdout.flush()

def draw_header():
    w   = W()
    bar = col("─" * w, Fore.GREEN)
    print(bar)
    print(col("NormansChat".center(w), Fore.GREEN, Style.BRIGHT))
    print(bar)
    name_part   = col(f"  💬 {partner_name}", Fore.WHITE, Style.BRIGHT)
    online_part = col("● ONLINE  ", Fore.GREEN)
    gap = max(w - len(f"  💬 {partner_name}") - len("● ONLINE  "), 1)
    print(f"{name_part}{' ' * gap}{online_part}")
    print(bar)
    print(col("  Type /quit to leave\n", Fore.WHITE))

# ── Protocol parser ───────────────────────────────────────────────────────────
# Server sends lines in format:
#   SYS:<message>
#   PROMPT:name  or  PROMPT:room
#   CONNECTED:<partner_name>
#   MSG:<sender_name>:<message>

def handle_line(line):
    """Parse a server line and update UI accordingly."""
    global chat_active, partner_name

    if line.startswith("MSG:"):
        # Chat message from partner
        rest   = line[4:]
        parts  = rest.split(":", 1)
        if len(parts) == 2:
            sender, msg = parts[0].strip(), parts[1].strip()
            print_theirs(sender, msg)
            show_prompt()

    elif line.startswith("CONNECTED:"):
        pname = line[10:].strip()
        if pname:
            partner_name = pname
        chat_active = True
        print_system(f"Connected! Chatting with {partner_name}.")
        print_system("Type /quit to leave.")
        show_prompt()

    elif line.startswith("SYS:"):
        msg = line[4:].strip()
        print_system(msg)
        if chat_active:
            show_prompt()

    elif line.startswith("PROMPT:"):
        # handled in setup phase, ignore here
        pass

# ── Receive thread ────────────────────────────────────────────────────────────

def receive(sock):
    buf = ""
    try:
        while not stop_event.is_set():
            try:
                sock.settimeout(1.0)
                data = sock.recv(4096)
            except socket.timeout:
                continue
            except:
                break

            if not data:
                break

            buf += data.decode(errors="ignore")

            while "\n" in buf:
                line, buf = buf.split("\n", 1)
                line = line.strip()
                if line:
                    handle_line(line)
    except:
        pass
    finally:
        stop_event.set()

# ── Send thread ───────────────────────────────────────────────────────────────

def send(sock):
    try:
        while not stop_event.is_set():
            if chat_active:
                show_prompt()
            try:
                msg = input("")
            except (EOFError, KeyboardInterrupt):
                break

            msg = msg.strip()
            if not msg or stop_event.is_set():
                continue

            try:
                sock.send((msg + "\n").encode())
            except:
                break

            if msg.lower() == "/quit":
                break

            # Print OUR message on the left — only once right here
            if chat_active:
                print_mine(msg)

    except:
        pass
    finally:
        stop_event.set()

# ── Setup: name + room ────────────────────────────────────────────────────────

def setup(sock):
    """Handle name and room prompts before chat starts."""
    global my_name
    buf  = ""
    step = 0  # 0=waiting for name prompt, 1=waiting for room prompt, 2=done

    while step < 2:
        try:
            sock.settimeout(15.0)
            data = sock.recv(4096)
        except:
            return False
        if not data:
            return False

        buf += data.decode(errors="ignore")

        # Process any complete lines
        while "\n" in buf:
            line, buf = buf.split("\n", 1)
            line = line.strip()
            if not line:
                continue

            if line.startswith("PROMPT:name") and step == 0:
                sys.stdout.write(col("\n  Enter your display name: ", Fore.GREEN))
                sys.stdout.flush()
                name = input("")
                my_name = name.strip()
                sock.send((my_name + "\n").encode())
                step = 1

            elif line.startswith("PROMPT:room") and step == 1:
                sys.stdout.write(col("\n  Enter Room ID to create or join: ", Fore.GREEN))
                sys.stdout.flush()
                room = input("")
                sock.send((room.strip() + "\n").encode())
                step = 2

            elif line.startswith("SYS:"):
                msg = line[4:].strip()
                sys.stdout.write(f"\n  {col(msg, Fore.YELLOW)}\n")
                sys.stdout.flush()

    return True

# ── Main ──────────────────────────────────────────────────────────────────────

def main():
    global my_name

    if os.name == "nt":
        os.system("")

    if not HAS_COLOR:
        print("  Tip: pip install colorama  for colors\n")

    clear_screen()
    print(col("""
  ╔══════════════════════════════╗
  ║       NormansChat            ║
  ║   Terminal Messenger         ║
  ╚══════════════════════════════╝
""", Fore.GREEN, Style.BRIGHT))

    print(col("  Connecting to server...", Fore.WHITE))

    try:
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.connect((SERVER_HOST, SERVER_PORT))
    except Exception as e:
        print(col(f"\n  [ERROR] Cannot connect: {e}", Fore.RED))
        input("\n  Press Enter to exit...")
        sys.exit(1)

    print(col("  Connected!\n", Fore.GREEN))

    if not setup(sock):
        print(col("\n  Lost connection during setup.", Fore.RED))
        sys.exit(1)

    # Wait for server to confirm partner connection
    import time
    print(col("\n  Waiting for partner to join...", Fore.YELLOW))

    # Keep reading until CONNECTED message
    buf = ""
    while True:
        try:
            sock.settimeout(TIMEOUT_SECONDS_CLIENT := 610)
            data = sock.recv(4096)
        except:
            break
        if not data:
            break
        buf += data.decode(errors="ignore")
        while "\n" in buf:
            line, buf = buf.split("\n", 1)
            line = line.strip()
            if not line:
                continue
            if line.startswith("SYS:"):
                print(f"  {col(line[4:], Fore.YELLOW)}")
            elif line.startswith("CONNECTED:"):
                pname = line[10:].strip()
                if pname:
                    import importlib
                    globals()["partner_name"] = pname
                break
        else:
            continue
        break

    import time
    time.sleep(0.3)

    # Draw chat UI
    clear_screen()
    draw_header()

    t1 = threading.Thread(target=receive, args=(sock,), daemon=True)
    t2 = threading.Thread(target=send,    args=(sock,), daemon=True)
    t1.start()
    t2.start()

    try:
        stop_event.wait()
    except KeyboardInterrupt:
        pass

    print(col("\n\n  Goodbye! 👋\n", Fore.GREEN, Style.BRIGHT))
    try:
        sock.close()
    except:
        pass


if __name__ == "__main__":
    main()