from __future__ import annotations

import time

from rich import box
from rich.console import Group
from rich.table import Table

from hytop.core.format import fmt_elapsed, fmt_window
from hytop.core.history import SlidingHistory
from hytop.net.models import NetCounter, RateSample


def format_rate(value: float, iec: bool = False) -> str:
    if iec:
        units = ["B/s", "KiB/s", "MiB/s", "GiB/s", "TiB/s"]
        base = 1024.0
    else:
        units = ["B/s", "kB/s", "MB/s", "GB/s", "TB/s"]
        base = 1000.0
    output = float(value)
    idx = 0
    while output >= base and idx < len(units) - 1:
        output /= base
        idx += 1
    return f"{output:7.2f} {units[idx]}"


def split_iface_key(iface_key: str) -> tuple[str, str]:
    kind, _, name = iface_key.partition(":")
    return kind, name


def format_iface_name(name: str, link_state: str | None) -> str:
    if not link_state:
        return name
    normalized = link_state.strip().lower()
    is_down = normalized in {"down", "disabled", "init", "inactive"} or normalized.startswith(
        "down"
    )
    if is_down:
        return f"{name} (down)"
    return name


def build_renderable(
    window: float,
    hosts: list[str],
    histories: dict[tuple[str, str], SlidingHistory],
    monitored_keys: set[tuple[str, str]],
    latest_counter_by_key: dict[tuple[str, str], NetCounter],
    errors: dict[str, str],
    poll_interval: float,
    elapsed_since_start: float,
    iec: bool = False,
) -> Group:
    now = time.monotonic()
    host_rank = {host: idx for idx, host in enumerate(hosts)}
    key_list = sorted(monitored_keys, key=lambda x: (host_rank.get(x[0], len(hosts)), x[1]))

    title = (
        f"hytop net | interval={poll_interval:.2f}s | elapsed={fmt_elapsed(elapsed_since_start)}"
    )
    table = Table(title=title, box=box.MINIMAL_HEAVY_HEAD, expand=True)
    table.add_column("Host", justify="left", no_wrap=True)
    table.add_column("Mode", justify="left")
    table.add_column("Device", justify="left")
    table.add_column("NIC", justify="left")
    table.add_column("RX", justify="right")
    table.add_column("TX", justify="right")
    table.add_column(f"RX@{fmt_window(window)}", justify="right")
    table.add_column(f"TX@{fmt_window(window)}", justify="right")

    for key in key_list:
        history = histories.get(key)
        latest_counter = latest_counter_by_key.get(key)
        if history is None or latest_counter is None:
            continue
        latest_rate = history.latest()
        if latest_rate is None:
            continue
        if not isinstance(latest_rate, RateSample):
            continue
        stale = (now - latest_rate.ts) > window
        host, iface_key = key
        mode, name = split_iface_key(iface_key)
        device_text = latest_counter.device_name or name
        nic_text = format_iface_name(name, latest_counter.link_state)
        if stale:
            table.add_row(host, mode, device_text, nic_text, "-", "-", "-", "-")
            continue

        rx_avg = history.avg("rx_bps", window, now)
        tx_avg = history.avg("tx_bps", window, now)
        table.add_row(
            host,
            mode,
            device_text,
            nic_text,
            format_rate(latest_rate.rx_bps, iec),
            format_rate(latest_rate.tx_bps, iec),
            format_rate(rx_avg, iec),
            format_rate(tx_avg, iec),
        )

    if table.row_count == 0:
        table.add_row("No data yet.", "", "", "", "", "", "", "")

    if not errors:
        return Group(table)
    err_table = Table(title="Host errors", box=box.MINIMAL_HEAVY_HEAD, expand=True)
    err_table.add_column("Host", justify="left", no_wrap=True)
    err_table.add_column("Error", justify="left")
    for host in hosts:
        err = errors.get(host)
        if err:
            err_table.add_row(host, err)
    return Group(table, err_table)
