def fw_events(file, filter_ip, filter_type, clients_dir, net_file, limit):
    """
    Format firewall drop events with dedup, counts, and service annotation.
    Output per line: ts|client|dest_ip|dest_port|proto|service_name|count
    """
    import glob
    from datetime import datetime
    proto_map = {1: 'icmp', 6: 'tcp', 17: 'udp'}

    # Build ip->name map
    ip_to_name = {}
    for conf in glob.glob(f"{clients_dir}/*.conf"):
        name = os.path.basename(conf).replace('.conf', '')
        try:
            with open(conf) as f:
                for line in f:
                    if line.startswith('Address'):
                        ip = line.split('=')[1].strip().split('/')[0]
                        ip_to_name[ip] = name
        except Exception:
            pass

    # Load net services for reverse lookup — independent of rest of function
    net_data = {}
    if net_file and os.path.exists(net_file):
        try:
            with open(net_file) as f:
                net_data = json.load(f)
        except Exception:
            pass

    def reverse_lookup(dest_ip, dest_port, proto):
        for svc_name, svc in net_data.items():
            if not isinstance(svc, dict):
                continue
            if svc.get('ip', '') != dest_ip:
                continue
            ports = svc.get('ports', {})
            if dest_port:
                for port_name, port_def in ports.items():
                    if not isinstance(port_def, dict):
                        continue
                    if (str(port_def.get('port', '')) == str(dest_port) and
                            port_def.get('proto', 'tcp') == proto):
                        return f"{svc_name}:{port_name}"
                return svc_name
            return svc_name
        return ''

    # Parse and first-pass dedup (within time window per key)
    events = []
    last_seen = {}

    try:
        with open(file) as f:
            for line in f:
                try:
                    e = json.loads(line.strip())
                    src = e.get('src_ip', '')
                    if not src:
                        continue
                    if filter_ip and src != filter_ip:
                        continue

                    proto_num = int(e.get('ip.protocol', 0))
                    proto = proto_map.get(proto_num, str(proto_num))
                    dst  = e.get('dest_ip', '')
                    port = str(e.get('dest_port', ''))
                    key  = (src, dst, port, proto_num)

                    ts_str = e.get('timestamp', '')
                    try:
                        ts = datetime.fromisoformat(ts_str).timestamp()
                    except Exception:
                        ts = 0

                    windows = {1: 5, 6: 30, 17: 10}
                    window  = windows.get(proto_num, 10)
                    if key in last_seen and (ts - last_seen[key]) < window:
                        continue
def wg_events(file, filter_client, filter_type, limit):
    """
    Format WireGuard events with dedup and counts.
    Output per line: ts|client|endpoint|event|count
    """
    from datetime import datetime

    events = []
    try:
        with open(file) as f:
            for line in f:
                try:
                    e = json.loads(line.strip())
                    client = e.get('client', '')
                    if not client:
                        continue
                    if filter_client and client != filter_client:
                        continue
                    if filter_type and not client.startswith(filter_type + '-'):
                        continue
                    events.append(e)
                except Exception:
                    pass
    except Exception:
        pass

    # Dedup consecutive same client+event+endpoint within 60s
    deduped = []
    counts  = []
    for e in events:
        ts_str   = e.get('timestamp', '')
        try:
            ts = datetime.fromisoformat(ts_str).timestamp()
        except Exception:
            ts = 0
        client   = e.get('client', '')
        event    = e.get('event', '')
        endpoint = e.get('endpoint', '')
        key      = (client, event, endpoint[:15])

        if deduped:
            prev     = deduped[-1]
            prev_ts_str = prev.get('timestamp', '')
            try:
                prev_ts = datetime.fromisoformat(prev_ts_str).timestamp()
            except Exception:
                prev_ts = 0
            prev_key = (
                prev.get('client', ''),
                prev.get('event', ''),
                prev.get('endpoint', '')[:15]
            )
            if key == prev_key and (ts - prev_ts) < 300:
                counts[-1] += 1
                continue

        deduped.append(e)
        counts.append(1)

    limit = int(limit) if limit else 50
