#!/usr/bin/env python3
"""Minimal IRC test client -- connect, send commands, print responses."""

from __future__ import annotations

import argparse
import selectors
import socket
import ssl
import sys
import time


def connect(host: str, port: int, password: str, nick: str, tls: bool) -> socket.socket:
    """Connect to IRC server, register, return socket."""
    raw = socket.create_connection((host, port), timeout=10)
    if tls:
        ctx = ssl.create_default_context()
        ctx.check_hostname = False
        ctx.verify_mode = ssl.CERT_NONE
        sock = ctx.wrap_socket(raw, server_hostname=host)
    else:
        sock = raw
    sock.setblocking(False)

    def send(line: str) -> None:
        sock.sendall(f"{line}\r\n".encode())

    # Register
    if password:
        send(f"PASS {password}")
    send(f"NICK {nick}")
    send(f"USER {nick} 0 * :{nick}")
    return sock


def run(sock: socket.socket, channel: str, nick: str, commands: list[str],
        delay: float, wait: float) -> None:
    """Join channel, send commands, print PRIVMSG responses."""
    sel = selectors.DefaultSelector()
    sel.register(sock, selectors.EVENT_READ)
    buf = ""
    joined = False
    ready = False  # True after bot's WHO completes
    cmd_idx = 0
    last_send = 0.0
    deadline = 0.0

    def send(line: str) -> None:
        sock.sendall(f"{line}\r\n".encode())

    # Join channel
    time.sleep(1.5)
    send(f"JOIN {channel}")

    start = time.monotonic()
    timeout = 60.0  # hard cap

    while time.monotonic() - start < timeout:
        events = sel.select(timeout=0.5)
        for key, _ in events:
            data = key.fileobj.recv(4096).decode("utf-8", errors="replace")
            if not data:
                print("-- connection closed --", file=sys.stderr)
                return
            buf += data
            while "\r\n" in buf:
                line, buf = buf.split("\r\n", 1)
                # Handle PING
                if line.startswith("PING"):
                    send(f"PONG {line[5:]}")
                    continue
                # Detect our join complete (End of NAMES)
                if " 366 " in line:
                    joined = True
                    print(f"-- joined {channel} --", file=sys.stderr)
                # Detect bot's WHO complete (End of WHO list)
                # 315 = RPL_ENDOFWHO -- bot sends WHO on join
                if " 315 " in line and joined and not ready:
                    ready = True
                    print("-- bot WHO complete, sending commands --",
                          file=sys.stderr)
                # Print PRIVMSG from bot (not from us)
                if "PRIVMSG" in line and f":{nick}!" not in line:
                    parts = line.split(" ", 3)
                    if len(parts) >= 4:
                        sender = parts[0].split("!")[0].lstrip(":")
                        msg = parts[3].lstrip(":")
                        print(f"\033[2m{sender}>\033[0m {msg}")

        # Send commands after bot finishes WHO
        if ready and cmd_idx < len(commands):
            now = time.monotonic()
            if now - last_send >= delay:
                cmd = commands[cmd_idx]
                send(f"PRIVMSG {channel} :{cmd}")
                print(f"\033[33m  --> {cmd}\033[0m", file=sys.stderr)
                last_send = now
                cmd_idx += 1
                if cmd_idx >= len(commands):
                    deadline = now + wait

        # Fallback: if no WHO seen after 5s, start sending anyway
        if joined and not ready and (time.monotonic() - start) > 8:
            ready = True
            print("-- WHO timeout, sending anyway --", file=sys.stderr)

        # Exit after wait period following last command
        if deadline and time.monotonic() > deadline:
            break

    send("QUIT :done")
    sel.unregister(sock)
    sock.close()


def main() -> None:
    ap = argparse.ArgumentParser(description="IRC test client")
    ap.add_argument("commands", nargs="*", default=["!twitch list", "!yt list"],
                    help="Commands to send (default: !twitch list, !yt list)")
    ap.add_argument("-H", "--host", default="mymx.me")
    ap.add_argument("-p", "--port", type=int, default=6697)
    ap.add_argument("-P", "--password", default="irc$1234=")
    ap.add_argument("-n", "--nick", default="tester")
    ap.add_argument("-c", "--channel", default="#derp")
    ap.add_argument("--no-tls", action="store_true")
    ap.add_argument("-d", "--delay", type=float, default=2.0,
                    help="Seconds between commands (default: 2)")
    ap.add_argument("-w", "--wait", type=float, default=8.0,
                    help="Seconds to wait after last command (default: 8)")
    args = ap.parse_args()

    sock = connect(args.host, args.port, args.password, args.nick,
                   not args.no_tls)
    try:
        run(sock, args.channel, args.nick, args.commands, args.delay, args.wait)
    except KeyboardInterrupt:
        sock.close()


if __name__ == "__main__":
    main()
