from __future__ import annotations

from collections.abc import Sequence

from .protocol import normalize_protocol, protocol_context
from .quantitative import bytes_phrase, duration_phrase, packets_phrase, rate_phrase
from .roles import dst_role_phrase, src_role_phrase
from .services import dst_service_phrase, src_port_category
from .tcp import handshake_story, tcp_flags_story


def join_clauses(parts: list[str]) -> str:
    """Join a list of sentence fragments into a readable phrase."""
    parts = [str(p).strip().rstrip(".") for p in parts if p and str(p).strip()]
    if not parts:
        return ""
    if len(parts) == 1:
        return parts[0]
    return ", ".join(parts[:-1]) + " and " + parts[-1]


def quantitative_clause(row: Sequence) -> str:
    """Return a phrase describing duration, size, and rate aspects of the flow."""
    dur = duration_phrase(getattr(row, "duration", None)) if hasattr(row, "duration") else ""
    byt = bytes_phrase(getattr(row, "total_payload_bytes", None)) if hasattr(row, "total_payload_bytes") else ""
    pck = packets_phrase(getattr(row, "packets_count", None)) if hasattr(row, "packets_count") else ""
    br = getattr(row, "bytes_rate", None) if hasattr(row, "bytes_rate") else None
    pr = getattr(row, "packets_rate", None) if hasattr(row, "packets_rate") else None
    rtm = rate_phrase(br, pr) if (br is not None or pr is not None) else ""

    def _clean(s: str) -> str:
        return s.rstrip(". ") if isinstance(s, str) else s

    dur, byt, pck, rtm = map(_clean, (dur, byt, pck, rtm))
    details = join_clauses([byt, pck])
    prefix = "The exchange occurred"
    if dur:
        prefix += f" {dur}"
    if details and rtm:
        return f"{prefix}, transferring {details} at a steady pace"
    if details:
        return f"{prefix}, transferring {details}"
    if rtm:
        return f"{prefix} {rtm}"
    return prefix


def build_sentence(row: Sequence) -> str:
    """Build a complete English sentence describing a network flow."""
    proto = normalize_protocol(getattr(row, "protocol", "TCP"))
    ctx = protocol_context(proto)
    src_role = src_role_phrase(getattr(row, "src_ip", "")) if hasattr(row, "src_ip") else None
    dst_role = dst_role_phrase(getattr(row, "dst_ip", "")) if hasattr(row, "dst_ip") else None
    src_port_cat = src_port_category(getattr(row, "src_port", None)) if hasattr(row, "src_port") else None
    dst_service_cat = dst_service_phrase(getattr(row, "dst_port", None)) if hasattr(row, "dst_port") else None

    intro_parts = [f"{ctx['article']} {proto} {ctx['noun']}"]
    if src_role:
        intro_parts.append(f"initiated by {src_role}")
    middle = []
    if src_port_cat:
        middle.append(f"using {src_port_cat}")
    if dst_service_cat and dst_role:
        middle.append(f"towards {dst_service_cat} on {dst_role}")
    elif dst_service_cat:
        middle.append(f"towards {dst_service_cat}")
    elif dst_role:
        middle.append(f"towards {dst_role}")
    first_sentence = join_clauses(intro_parts + middle).rstrip(".") + "."

    q = quantitative_clause(row)
    flags_str = tcp_flags_story(getattr(row, "tcp_flags", None)) if ctx["has_tcp_flags"] and hasattr(row, "tcp_flags") else ""
    hs_str = handshake_story(getattr(row, "handshake_state", "")) if ctx["has_tcp_handshake"] and hasattr(row, "handshake_state") else ""
    extras = [s.strip().rstrip(".") for s in (flags_str, hs_str) if s and s.strip()]

    if q and extras:
        second_sentence = q + ", " + ", ".join(extras) + "."
    elif q:
        second_sentence = q + "."
    elif extras:
        second_sentence = ", ".join(extras) + "."
    else:
        second_sentence = ""

    return (first_sentence + (" " + second_sentence if second_sentence else "")).strip()
