"""Tool to display live CAN/vCAN message stats, similar to "top"."""
import argparse
import time
import os
from importlib import metadata
from feathersdk.comms.wirehawk import WireHawk, WireHawkConfig, WireHawkSnapshot
from feathersdk.utils.common import timediff, currtime
from feathersdk.comms.comms_manager import SendCanMessage, SendTCPMessage


# SemVer-style version string for the CLI. Prefer package metadata so it stays in sync with releases.
try:
    __version__ = metadata.version("feathersdk")
except metadata.PackageNotFoundError:  # pragma: no cover
    __version__ = "0.0.0+unknown"

# Calculate the rate over the last N seconds
_RATE_WINDOW_SECONDS = 2.0


def _calc_rate(messages: list[SendCanMessage | SendTCPMessage]) -> float:
    messages = [msg for msg in messages if msg.timestamp > currtime() - _RATE_WINDOW_SECONDS]
    return len(messages) / _RATE_WINDOW_SECONDS


def _format_age(latest: float) -> str:
    if latest < 0:
        return "None"
    return f"{timediff(latest):.2f}s"


def _render_endpoint(stats: WireHawkSnapshot) -> None:
    send_count = len(stats.most_recent_sent_messages)
    recv_count = len(stats.most_recent_recv_messages)
    send_rate = _calc_rate(stats.most_recent_sent_messages)
    recv_rate = _calc_rate(stats.most_recent_recv_messages)
    send_age = _format_age(stats.latest_send_time)
    recv_age = _format_age(stats.latest_recv_time)
    send_ex = stats.most_recent_sent_messages[-1] if send_count > 0 else None
    recv_ex = stats.most_recent_recv_messages[-1] if recv_count > 0 else None

    print(f"{stats.endpoint} [{stats.endpoint_uid}]")
    print(f"\tsend: count={send_count}, rate={send_rate:.2f}/s, last_age={send_age}, latest message:\n\t\t{send_ex}")
    print(f"\trecv: count={recv_count}, rate={recv_rate:.2f}/s, last_age={recv_age}, latest message:\n\t\t{recv_ex}")


def _parse_args() -> argparse.Namespace:
    """Parse command line arguments."""
    parser = argparse.ArgumentParser(description="Live CAN/vCAN stats viewer (like top).")
    parser.add_argument("--version", action="version", version=f"wirehawk {__version__}")
    parser.add_argument("--interval", type=float, default=0.2, help="Update interval in seconds. Minimum is 0.05s.")
    args = parser.parse_args()

    update_interval = max(args.interval, 0.05)
    return update_interval, WireHawkConfig(log_dir=None)


def main() -> None:
    """Main function for WireHawk."""
    update_interval, wirehawk_configs = _parse_args()

    try:
        wirehawk = WireHawk(wirehawk_configs)
        init_time = currtime()
        while True:
            os.system("clear")  # Clear the screen
            print(f"WireHawk [v{__version__}] (update every {update_interval:.1f}s) - {currtime() - init_time:.2f}s elapsed. Ctrl+C to exit\n")

            # Add any new endpoints first, then render them all (also rendering any dropped endpoints)
            for endpoint in sorted(wirehawk.stats.keys()):
                _render_endpoint(wirehawk.stats[endpoint].get_last_stats_snapshot())
                print()
            time.sleep(update_interval)
    except KeyboardInterrupt:
        pass


if __name__ == "__main__":
    main()
