import Network from "gi://AstalNetwork";
import { bind, Variable } from "astal";
import { toHumanReadable } from "@/lib/utils";

type IpOutput = Array<{
    ifname: string;
    stats64: {
        rx: { bytes: number };
        tx: { bytes: number };
    };
}>;

type Usage = {
    initialRx: number | null;
    initialTx: number | null;
    totalRx: number;
    totalTx: number;
};

const getConnection = (instance?: Network.Wifi | Network.Wired) => {
    if (!instance) return null;

    const addresses = (instance instanceof Network.Wifi
        ? instance?.activeConnection
        : instance?.connection)
        ?.ip4Config?.get_addresses();
    return addresses.map(addr => addr.get_address()).join(", ");
}

export default function NetworkInfo() {
    const net = Network.get_default();
    const wiredConn = bind(net, "wired");
    const wifiConn = bind(net, "wifi");

    const currentConn = Variable.derive([
        wiredConn,
        wifiConn
    ], (wired, wifi) => {
        switch (net.primary) {
            case Network.Primary.WIFI: return wifi;
            case Network.Primary.WIRED: return wired;
            default: return;
        }
    })

    const networkInterface = net.primary == 1 ? "enp0s31f6" : "wlp4s0";
    const networkUsage = Variable<Usage>({
        initialRx: null,
        initialTx: null,
        totalRx: 0,
        totalTx: 0,
    }).poll(
        5000,
        ['ip', '-s', '-j', 'link', 'show', networkInterface],
        (out, prev) => {
            const ipOutput = JSON.parse(out) as IpOutput;
            const totalRx = ipOutput?.reduce((prev, item) => prev + item.stats64.rx.bytes, 0);
            const totalTx = ipOutput?.reduce((prev, item) => prev + item.stats64.tx.bytes, 0);

            return {
                initialRx: prev.initialRx ?? totalRx,
                initialTx: prev.initialTx ?? totalTx,
                totalTx,
                totalRx,
            };
        }
    );

    const dataBinding = bind(networkUsage);
    const binding = bind(currentConn);

    return binding.as(Boolean) ? <box spacing={3}>
        {binding.as(getConnection)}
        TX: {dataBinding.as(data => toHumanReadable(data.totalTx / 4, "B/s"))}
        RX: {dataBinding.as(data => toHumanReadable(data.totalRx / 4, "B/s"))}
    </box> : <label>No network</label>
}