#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
监听指定端口，用于观察是否有 TCP/UDP 流量进入本机。
"""

from __future__ import annotations

import socket
import sys
import time


def _validate_port(port: int) -> bool:
    return 1 <= port <= 65535


def command_port_test(port: int, udp: bool = False, bind_host: str = "0.0.0.0") -> None:
    """在 bind_host:port 上监听；有连接或收到数据时打印信息，Ctrl+C 退出。"""
    if not _validate_port(port):
        print("错误: 端口号应为 1-65535 的整数", file=sys.stderr)
        sys.exit(1)

    if udp:
        _run_udp(bind_host, port)
    else:
        _run_tcp(bind_host, port)


def _run_tcp(host: str, port: int) -> None:
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    try:
        sock.bind((host, port))
        sock.listen(8)
    except OSError as e:
        print(f"TCP 监听失败 [{host}]:{port}: {e}", file=sys.stderr)
        sys.exit(1)

    print(f"TCP 监听 {host}:{port}，等待连接…（Ctrl+C 退出）")
    try:
        while True:
            conn, addr = sock.accept()
            ts = time.strftime("%Y-%m-%d %H:%M:%S")
            print(f"[{ts}] 收到 TCP 连接: {addr[0]}:{addr[1]}")
            try:
                conn.settimeout(2.0)
                chunk = conn.recv(4096)
                if chunk:
                    preview = chunk[:200]
                    try:
                        text = preview.decode("utf-8", errors="replace")
                    except Exception:
                        text = repr(preview)
                    print(f"  首包长度: {len(chunk)} 字节，预览: {text!r}")
                else:
                    print("  （对端关闭，无数据）")
            except socket.timeout:
                print("  （2 秒内无数据）")
            except OSError as e:
                print(f"  读取异常: {e}")
            finally:
                try:
                    conn.close()
                except OSError:
                    pass
    except KeyboardInterrupt:
        print("\n已停止。")
    finally:
        sock.close()


def _run_udp(host: str, port: int) -> None:
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    try:
        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    except OSError:
        pass
    try:
        sock.bind((host, port))
    except OSError as e:
        print(f"UDP 监听失败 [{host}]:{port}: {e}", file=sys.stderr)
        sys.exit(1)

    print(f"UDP 监听 {host}:{port}，等待数据报…（Ctrl+C 退出）")
    try:
        while True:
            data, addr = sock.recvfrom(65535)
            ts = time.strftime("%Y-%m-%d %H:%M:%S")
            print(f"[{ts}] 收到 UDP 来自 {addr[0]}:{addr[1]}，长度 {len(data)} 字节")
            preview = data[:200]
            try:
                text = preview.decode("utf-8", errors="replace")
            except Exception:
                text = repr(preview)
            print(f"  预览: {text!r}")
    except KeyboardInterrupt:
        print("\n已停止。")
    finally:
        sock.close()
