"""Mapeo puro de número de puerto a servicio IANA well-known.

Tabla estática embebida que asocia (puerto, protocolo) con el servicio que la
convención IANA / nmap-services espera típicamente en ese puerto, más una
descripción corta. Sin red, sin I/O: dado un puerto abierto detectado por un
scanner, esta función dice qué servicio se ESPERA ahí por convención, no lo
verifica en vivo.
"""

# (port, proto) -> (service, description)
WELL_KNOWN: dict[tuple[int, str], tuple[str, str]] = {
    (20, "tcp"): ("ftp-data", "FTP Data Transfer"),
    (21, "tcp"): ("ftp", "File Transfer Protocol (control)"),
    (22, "tcp"): ("ssh", "Secure Shell"),
    (23, "tcp"): ("telnet", "Telnet"),
    (25, "tcp"): ("smtp", "Simple Mail Transfer Protocol"),
    (37, "tcp"): ("time", "Time Protocol"),
    (43, "tcp"): ("whois", "WHOIS Directory Service"),
    (53, "tcp"): ("dns", "Domain Name System (zone transfer)"),
    (53, "udp"): ("dns", "Domain Name System"),
    (67, "udp"): ("dhcp", "DHCP Server (BOOTP)"),
    (68, "udp"): ("dhcp", "DHCP Client (BOOTP)"),
    (69, "udp"): ("tftp", "Trivial File Transfer Protocol"),
    (79, "tcp"): ("finger", "Finger Protocol"),
    (80, "tcp"): ("http", "Hypertext Transfer Protocol"),
    (88, "tcp"): ("kerberos", "Kerberos Authentication"),
    (110, "tcp"): ("pop3", "Post Office Protocol v3"),
    (111, "tcp"): ("rpcbind", "ONC RPC / Portmapper"),
    (111, "udp"): ("rpcbind", "ONC RPC / Portmapper"),
    (113, "tcp"): ("ident", "Ident / Auth Service"),
    (119, "tcp"): ("nntp", "Network News Transfer Protocol"),
    (123, "udp"): ("ntp", "Network Time Protocol"),
    (135, "tcp"): ("msrpc", "Microsoft RPC Endpoint Mapper"),
    (137, "udp"): ("netbios-ns", "NetBIOS Name Service"),
    (138, "udp"): ("netbios-dgm", "NetBIOS Datagram Service"),
    (139, "tcp"): ("netbios-ssn", "NetBIOS Session Service"),
    (143, "tcp"): ("imap", "Internet Message Access Protocol"),
    (161, "udp"): ("snmp", "Simple Network Management Protocol"),
    (162, "udp"): ("snmptrap", "SNMP Trap"),
    (177, "udp"): ("xdmcp", "X Display Manager Control Protocol"),
    (179, "tcp"): ("bgp", "Border Gateway Protocol"),
    (389, "tcp"): ("ldap", "Lightweight Directory Access Protocol"),
    (427, "tcp"): ("svrloc", "Service Location Protocol"),
    (443, "tcp"): ("https", "HTTP over TLS/SSL"),
    (445, "tcp"): ("smb", "SMB / Microsoft Directory Services"),
    (464, "tcp"): ("kpasswd", "Kerberos Password Change"),
    (465, "tcp"): ("smtps", "SMTP over TLS/SSL"),
    (500, "udp"): ("isakmp", "ISAKMP / IKE (IPsec)"),
    (512, "tcp"): ("exec", "Remote Process Execution (rexec)"),
    (513, "tcp"): ("login", "Remote Login (rlogin)"),
    (514, "tcp"): ("shell", "Remote Shell (rsh)"),
    (514, "udp"): ("syslog", "Syslog"),
    (515, "tcp"): ("printer", "Line Printer Daemon (LPD)"),
    (520, "udp"): ("rip", "Routing Information Protocol"),
    (523, "tcp"): ("ibm-db2", "IBM DB2"),
    (548, "tcp"): ("afp", "Apple Filing Protocol"),
    (554, "tcp"): ("rtsp", "Real Time Streaming Protocol"),
    (587, "tcp"): ("submission", "SMTP Mail Submission"),
    (623, "udp"): ("ipmi", "IPMI / RMCP"),
    (631, "tcp"): ("ipp", "Internet Printing Protocol"),
    (636, "tcp"): ("ldaps", "LDAP over TLS/SSL"),
    (873, "tcp"): ("rsync", "rsync File Synchronization"),
    (902, "tcp"): ("vmware", "VMware ESXi / Authentication"),
    (989, "tcp"): ("ftps-data", "FTP Data over TLS/SSL"),
    (990, "tcp"): ("ftps", "FTP over TLS/SSL"),
    (993, "tcp"): ("imaps", "IMAP over TLS/SSL"),
    (995, "tcp"): ("pop3s", "POP3 over TLS/SSL"),
    (1080, "tcp"): ("socks", "SOCKS Proxy"),
    (1194, "udp"): ("openvpn", "OpenVPN"),
    (1352, "tcp"): ("lotusnotes", "IBM Lotus Notes / Domino"),
    (1433, "tcp"): ("mssql", "Microsoft SQL Server"),
    (1434, "udp"): ("mssql-m", "Microsoft SQL Monitor"),
    (1521, "tcp"): ("oracle", "Oracle Database Listener"),
    (1723, "tcp"): ("pptp", "Point-to-Point Tunneling Protocol"),
    (1883, "tcp"): ("mqtt", "MQTT Message Broker"),
    (2049, "tcp"): ("nfs", "Network File System"),
    (2082, "tcp"): ("cpanel", "cPanel"),
    (2083, "tcp"): ("cpanel-ssl", "cPanel over TLS/SSL"),
    (2181, "tcp"): ("zookeeper", "Apache ZooKeeper"),
    (2375, "tcp"): ("docker", "Docker API (unencrypted)"),
    (2376, "tcp"): ("docker-ssl", "Docker API over TLS"),
    (2483, "tcp"): ("oracle-db", "Oracle DB (insecure)"),
    (2484, "tcp"): ("oracle-db-ssl", "Oracle DB over TLS/SSL"),
    (3000, "tcp"): ("dev-http", "Development HTTP / Grafana"),
    (3128, "tcp"): ("squid", "Squid HTTP Proxy"),
    (3268, "tcp"): ("globalcat", "LDAP Global Catalog"),
    (3306, "tcp"): ("mysql", "MySQL / MariaDB"),
    (3389, "tcp"): ("rdp", "Remote Desktop Protocol"),
    (3690, "tcp"): ("svn", "Subversion"),
    (4369, "tcp"): ("epmd", "Erlang Port Mapper Daemon"),
    (4444, "tcp"): ("metasploit", "Metasploit Default Listener"),
    (4505, "tcp"): ("saltstack", "SaltStack Publish"),
    (4506, "tcp"): ("saltstack", "SaltStack Request"),
    (5000, "tcp"): ("upnp", "UPnP / Flask Dev Server"),
    (5060, "udp"): ("sip", "Session Initiation Protocol"),
    (5061, "tcp"): ("sips", "SIP over TLS"),
    (5432, "tcp"): ("postgresql", "PostgreSQL Database"),
    (5601, "tcp"): ("kibana", "Kibana"),
    (5672, "tcp"): ("amqp", "Advanced Message Queuing (RabbitMQ)"),
    (5900, "tcp"): ("vnc", "Virtual Network Computing"),
    (5984, "tcp"): ("couchdb", "Apache CouchDB"),
    (5985, "tcp"): ("winrm", "Windows Remote Management (HTTP)"),
    (5986, "tcp"): ("winrm-ssl", "Windows Remote Management (HTTPS)"),
    (6379, "tcp"): ("redis", "Redis Key-Value Store"),
    (6443, "tcp"): ("kubernetes", "Kubernetes API Server"),
    (6660, "tcp"): ("irc", "Internet Relay Chat"),
    (6667, "tcp"): ("irc", "Internet Relay Chat"),
    (7001, "tcp"): ("weblogic", "Oracle WebLogic"),
    (8000, "tcp"): ("http-alt", "HTTP Alternate / Dev Server"),
    (8008, "tcp"): ("http-alt", "HTTP Alternate"),
    (8080, "tcp"): ("http-proxy", "HTTP Proxy / Alternate"),
    (8086, "tcp"): ("influxdb", "InfluxDB"),
    (8088, "tcp"): ("http-alt", "HTTP Alternate / Hadoop"),
    (8443, "tcp"): ("https-alt", "HTTPS Alternate"),
    (8500, "tcp"): ("consul", "HashiCorp Consul"),
    (8888, "tcp"): ("http-alt", "HTTP Alternate / Jupyter"),
    (9000, "tcp"): ("http-alt", "HTTP Alternate / PHP-FPM / SonarQube"),
    (9042, "tcp"): ("cassandra", "Apache Cassandra (CQL)"),
    (9092, "tcp"): ("kafka", "Apache Kafka Broker"),
    (9200, "tcp"): ("elasticsearch", "Elasticsearch HTTP"),
    (9300, "tcp"): ("elasticsearch", "Elasticsearch Transport"),
    (9418, "tcp"): ("git", "Git Protocol"),
    (9999, "tcp"): ("http-alt", "HTTP Alternate / Admin"),
    (10000, "tcp"): ("webmin", "Webmin Admin Panel"),
    (11211, "tcp"): ("memcached", "Memcached"),
    (15672, "tcp"): ("rabbitmq-mgmt", "RabbitMQ Management UI"),
    (27017, "tcp"): ("mongodb", "MongoDB Database"),
    (27018, "tcp"): ("mongodb", "MongoDB Shard"),
    (50000, "tcp"): ("sap", "SAP / DB2 DRDA"),
}


def identify_port_service(port: int, proto: str = "tcp") -> dict:
    """Identifica el servicio IANA well-known esperado en un puerto.

    Función pura: consulta una tabla estática embebida, sin red ni I/O. Indica
    qué servicio se ESPERA por convención en ese puerto, no verifica que sea el
    que realmente corre allí.

    Args:
        port: número de puerto (0-65535).
        proto: protocolo, "tcp" o "udp" (default "tcp"). Se normaliza a minúsculas.

    Returns:
        dict con claves:
            - port (int): el puerto consultado.
            - proto (str): el protocolo normalizado.
            - service (str): nombre del servicio; "unknown" si no está en la
              tabla; "invalid" si el puerto está fuera de rango o el protocolo
              no es tcp/udp.
            - description (str): descripción corta; "" cuando no se conoce.
            - known (bool): True solo si hay match en la tabla.

        Ejemplos de retorno:
            identify_port_service(22)
            -> {"port": 22, "proto": "tcp", "service": "ssh",
                "description": "Secure Shell", "known": True}
            identify_port_service(99999)
            -> {"port": 99999, "proto": "tcp", "service": "invalid",
                "description": "", "known": False}
    """
    proto_norm = str(proto).strip().lower()

    if not isinstance(port, int) or isinstance(port, bool):
        return {
            "port": port,
            "proto": proto_norm,
            "service": "invalid",
            "description": "",
            "known": False,
        }

    if port < 0 or port > 65535 or proto_norm not in ("tcp", "udp"):
        return {
            "port": port,
            "proto": proto_norm,
            "service": "invalid",
            "description": "",
            "known": False,
        }

    match = WELL_KNOWN.get((port, proto_norm))
    if match is None:
        return {
            "port": port,
            "proto": proto_norm,
            "service": "unknown",
            "description": "",
            "known": False,
        }

    service, description = match
    return {
        "port": port,
        "proto": proto_norm,
        "service": service,
        "description": description,
        "known": True,
    }


if __name__ == "__main__":
    import json

    for p, pr in [(22, "tcp"), (443, "tcp"), (53, "udp"), (3306, "tcp"), (99999, "tcp")]:
        print(json.dumps(identify_port_service(p, pr)))
