"""Resume una captura TCP: handshake, datos, ACK duplicados, retransmisiones, cierre.

    python3 tcpsum.py captures/clean-a.pcap

Las marcas de retransmisión las pone Wireshark por deducción: mira la captura desde afuera
y no sabe qué pasó dentro del kernel.
"""
import sys

from pcaptools import read_capture


def summarize(segments):
    """Calcula el resumen de la primera conexión de la captura."""
    first = segments[0]
    client = (first.src, first.sport)
    data = [s for s in segments if (s.src, s.sport) == client and s.len]
    back = [s for s in segments if (s.src, s.sport) != client]
    return {
        "client": f"{first.src}:{first.sport}",
        "server": f"{first.dst}:{first.dport}",
        "syn": next((s for s in segments if s.syn and not s.has_ack), None),
        "synack": next((s for s in segments if s.syn and s.has_ack), None),
        "data_segments": len(data),
        "data_bytes": sum(s.len for s in data),
        "new_bytes": max((s.seq + s.len - 1 for s in data), default=0),
        "largest": max((s.len for s in data), default=0),
        "acks": len([s for s in back if s.has_ack and not s.len and not s.syn]),
        "dup_acks": len([s for s in back if s.dup_ack]),
        "max_dup": max((s.dup_ack for s in back if s.dup_ack), default=0),
        "dup_reached": {level: len([s for s in back if s.dup_ack == level]) for level in (1, 2, 3)},
        "resent": len([s for s in data if s.resent]),
        "fast": len([s for s in data if s.fast]),
        "zero_window": len([s for s in back if s.zero_window]),
        "probes": len([s for s in segments if s.probe]),
        "max_window": max((s.win for s in back if s.win is not None and not s.syn), default=0),
        "fins": len([s for s in segments if s.fin]),
        "rsts": len([s for s in segments if s.rst]),
        "seconds": segments[-1].time,
    }


def describe_options(segment):
    return (f"MSS {segment.mss}, ventana {segment.win_field}, escala {segment.wscale}, "
            f"SACK {'sí' if segment.sack_perm else 'no'}, "
            f"timestamps {'sí' if segment.ts else 'no'}")


def main():
    if len(sys.argv) != 2:
        print(__doc__)
        return 2
    segments = read_capture(sys.argv[1])
    if not segments:
        print("la captura no tiene segmentos TCP")
        return 1
    info = summarize(segments)
    print(f"conexión       {info['client']} -> {info['server']}")
    if info["syn"]:
        print(f"SYN            seq real {info['syn'].seq_raw}; {describe_options(info['syn'])}")
    if info["synack"]:
        print(f"SYN-ACK        seq real {info['synack'].seq_raw}; {describe_options(info['synack'])}")
    print(f"datos          {info['data_segments']} segmentos, {info['data_bytes']} bytes enviados, "
          f"{info['new_bytes']} bytes distintos, el mayor de {info['largest']}")
    print(f"ACK de vuelta  {info['acks']} sin datos, ventana máxima anunciada {info['max_window']}")
    reached = info["dup_reached"]
    print(f"ACK duplicados {info['dup_acks']} (el mayor, #{info['max_dup']}); rachas que llegaron "
          f"a #1: {reached[1]}, a #2: {reached[2]}, a #3: {reached[3]}")
    print(f"retransmitidos {info['resent']} segmentos, {info['fast']} de ellos marcados como fast")
    print(f"ventana cero   {info['zero_window']} avisos, {info['probes']} sondas")
    print(f"cierre         {info['fins']} FIN, {info['rsts']} RST")
    print(f"duración       {info['seconds']:.3f} s")
    return 0


if __name__ == "__main__":
    sys.exit(main())
