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              

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

# ── 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")

# ── Message printers ──────────────────────────────────────────────────────────

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

def print_theirs(name, msg):
    """Their message — RIGHT side, grey bubble, no timestamp."""
    w = W()
    bubble   = col(f"  {msg}  ", Fore.WHITE, Back.BLUE)
    name_tag = col(name, Fore.CYAN, Style.BRIGHT)
    # visible length for padding (no ANSI codes)
    vis = len(name) + len(msg) + 4
    pad = max(w - vis - 2, 0)
    with print_lock:
        sys.stdout.write(f"\r\033[K{' ' * pad}{name_tag}\n")
        sys.stdout.write(f"\r\033[K{' ' * pad}{bubble}\n")
        sys.stdout.flush()

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

def show_prompt():
    prompt = col(f"  ✏  {my_name}: ", Fore.GREEN, Style.BRIGHT)
    sys.stdout.write(f"\r\033[K{prompt}")
    sys.stdout.flush()

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

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

def receive(sock, stop_event):
    global chat_active, partner_name
    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.replace("\r", "").strip()
                if not line:
                    continue

                # ── Skip ALL "You:" echoes from server
                if line.startswith("You:") or line == "You":
                    continue

                # ── Detect chat message: "Name: message"
                if chat_active and ": " in line and not line.startswith("[") and not line.startswith("-"):
                    parts  = line.split(": ", 1)
                    sender = parts[0].strip()
                    msg    = parts[1].strip()
                    if sender and msg and sender != my_name:
                        print_theirs(sender, msg)
                        show_prompt()
                        continue

                # ── Detect chat going active
                if "joined the room" in line or "Chatting with" in line or "Connected!" in line:
                    chat_active = True
                    if "Chatting with" in line:
                        p = line.split("Chatting with")[-1].strip().rstrip(".")
                        if p:
                            partner_name = p
                    elif "joined the room" in line:
                        p = line.replace("[","").replace("]","").replace("joined the room!","").strip()
                        if p:
                            partner_name = p

                # ── Skip dashes (decorative lines from server)
                if set(line.strip()) <= {"-", "─", "="}:
                    continue

                # ── Print system message
                print_system(line)
                if chat_active:
                    show_prompt()

    except:
        pass
    finally:
        stop_event.set()

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

def send(sock, stop_event):
    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 + "\r\n").encode())
            except:
                break

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

            # Print our OWN message once — left side green
            # Do NOT print if not in chat yet (setup phase)
            if chat_active:
                print_mine(msg)

    except:
        pass
    finally:
        stop_event.set()

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

def setup(sock):
    global my_name
    buf  = ""
    step = 0

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

        text = data.decode(errors="ignore")
        buf += text
        sys.stdout.write(text)
        sys.stdout.flush()

        if step == 0 and "display name" in buf.lower():
            name = input("")
            my_name = name.strip()
            sock.send((name + "\r\n").encode())
            step = 1

        elif step == 1 and "room id" in buf.lower():
            room = input("")
            sock.send((room + "\r\n").encode())
            step = 2

    return True

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

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

    if not HAS_COLOR:
        print("  Run 'pip install colorama' for colors!\n")

    clear_screen()
    print(col("\n  Welcome to NormansChat\n", Fore.GREEN, Style.BRIGHT))
    print(col("  Connecting to server...\n", 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)

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

    import time
    time.sleep(1.5)

    clear_screen()
    draw_header()

    stop_event = threading.Event()
    t1 = threading.Thread(target=receive, args=(sock, stop_event), daemon=True)
    t2 = threading.Thread(target=send,    args=(sock, stop_event), 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()