def fw_events(file, filter_ip, filter_type, clients_dir, limit):
    """Format firewall drop events with dedup and counts"""
    import glob
    proto_map = {1: 'icmp', 6: 'tcp', 17: 'udp'}

    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:
            pass

    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
                    dst    = e.get('dest_ip', '')
                    port   = e.get('dest_port', '')
                    proto  = e.get('ip.protocol', 0)
                    key    = (src, dst, port, proto)
                    ts_str = e.get('timestamp', '')
                    try:
                        from datetime import datetime
                        ts = datetime.fromisoformat(ts_str).timestamp()
                    except:
                        ts = 0
                    windows = {1: 5, 6: 30, 17: 10}
                    window  = windows.get(proto, 10)
                    if key in last_seen and (ts - last_seen[key]) < window:
                        continue
                    last_seen[key] = ts
                    events.append(e)
                except:
                    pass
    except:
        pass

    # Dedup consecutive same src+dst+port within 60s with count
    deduped = []
    counts  = []
    for e in events:
        ts_str = e.get('timestamp', '')
        try:
            from datetime import datetime
            ts = datetime.fromisoformat(ts_str).timestamp()
        except:
def wg_events(file, filter_client, filter_type, limit):
    """Format WireGuard events from events.log with dedup"""
    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:
                    pass
    except:
        pass

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

        if deduped and counts:
            prev = deduped[-1]
            prev_ts_str = prev.get('timestamp', '')
            try:
def format_fw_event(line, clients_dir):
    """Format a single fw_event line"""
    import glob
    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 l in f:
                    if l.startswith('Address'):
                        ip = l.split('=')[1].strip().split('/')[0]
                        ip_to_name[ip] = name
        except:
            pass
    
    try:
        e = json.loads(line.strip())
        src = e.get('src_ip', '')
        if not src:
            return None
        ts = e.get('timestamp', '')
        try:
            from datetime import datetime
            dt = datetime.fromisoformat(ts)
            ts = dt.strftime(DATETIME_FMT)
        except:
            pass