#!/usr/bin/env python3
"""Wrap `speedtest-cli` to cache/report test results."""

import json
import os
from pathlib import Path

import bitmath  # type: ignore[import-untyped]
import rich
import speedtest  # type: ignore[import-untyped]
import xdg
from libcli.basecli import BaseCLI
from libcli.basecmd import BaseCmd
from rich.box import ROUNDED
from rich.table import Table


class SpeedTestCmd(BaseCmd):
    """Speed `test` command class."""

    def init_command(self) -> None:
        """Initialize Speed `test` command instance."""

        self.add_subcommand_parser(
            "test",
            help="test internet bandwidth using `speedtest.net`",
            description="The `%(prog)s` command tests internet bandwidth using `speedtest.net`.",
        )

    def run(self) -> None:
        """Perform the command."""

        # get SSID of active connection
        ssid = os.popen("iwgetid -r").read().strip()
        print(f"ssid {ssid!r}")

        # run download test
        st = speedtest.Speedtest()
        st.get_best_server()
        st.download()
        print(st.results.json(pretty=self.options.pretty))

        # cache results
        if self.options.database:
            # augment results with ssid
            results = st.results.dict()
            results["ssid"] = ssid

            self.options.database.parent.mkdir(parents=True, exist_ok=True)
            with open(self.options.database, "a", encoding="utf-8") as db:
                print(json.dumps(results), file=db)


class SpeedReportCmd(BaseCmd):
    """Speed `report` command class."""

    def init_command(self) -> None:
        """Initialize Speed `report` command instance."""

        self.add_subcommand_parser(
            "report",
            help="print report",
            description=self.cli.dedent("""
        The `%(prog)s` command reads test results from `DATABASE`,
        and writes report to `stdout`.
                """),
        )

    def run(self) -> None:
        """Perform the command."""

        if not self.options.database:
            return

        table = Table(box=ROUNDED)
        for title in self.heading():
            if title[0] == "^":
                # PLW2901: title is stripped of its alignment prefix before use as column name.
                title, justify = title[1:], "center"  # noqa: PLW2901
            elif title[0] == ">":
                # PLW2901: title is stripped of its alignment prefix before use as column name.
                title, justify = title[1:], "right"  # noqa: PLW2901
            else:
                justify = "left"
            table.add_column(title, justify=justify)  # type: ignore[arg-type]

        with open(self.options.database, encoding="utf-8") as db:
            for line in db:
                results = json.loads(line)
                table.add_row(*self.detail(results))

        rich.print(table)

    @staticmethod
    def heading() -> list[str]:
        """Docstring."""

        return [
            "ssid",
            "timestamp",
            ">speedtest",
            ">kb",
            ">Mb",
            ">kB",
            ">KiB",
            ">MB",
            ">MiB",
        ]

    @staticmethod
    def detail(results: dict[str, str]) -> list[str]:
        """Docstring."""

        ssid = results["ssid"]
        timestamp = results["timestamp"]
        speedtest_bps = int(results["download"])
        b = bitmath.Bit(bits=speedtest_bps)

        # https://physics.nist.gov/cuu/Units/binary.html

        return [
            f"{ssid}",
            f"{timestamp}",
            f"{speedtest_bps:,}",
            f"{int(b.to_kb().value):,}",
            f"{int(b.to_Mb().value):,}",
            f"{int(b.to_kB().value):,}",
            f"{int(b.to_KiB().value):,}",
            f"{int(b.to_MB().value):,}",
            f"{int(b.to_MiB().value):,}",
        ]


class SpeedCLI(BaseCLI):
    """Command line interface."""

    def init_parser(self) -> None:
        """Initialize command line interface."""

        self.parser = self.ArgumentParser(
            description=self.dedent("""
        The `%(prog)s` command tests internet bandwidth using `speedtest.net`,
        saves test results to a database, and prints historical reports.
                """),
        )

    def add_arguments(self) -> None:
        """Add arguments to parser."""

        self.parser.set_defaults(pretty=True)

        group = self.parser.add_mutually_exclusive_group()

        arg = group.add_argument(
            "--database",
            default=Path(xdg.xdg_cache_home(), self.parser.prog, self.parser.prog + ".json"),
            type=Path,
            help="use `DATABASE`",
        )
        self.add_default_to_help(arg)

        arg = group.add_argument(
            "--no-database",
            dest="database",
            action="store_const",
            const=None,
            default=False,
            help="do not use `DATABASE`",
        )
        # self.add_default_to_help(arg)

        self.add_subcommand_classes([SpeedTestCmd, SpeedReportCmd])

    def main(self) -> None:
        """Command line interface entry point (method)."""

        if not self.options.cmd:
            self.parser.print_help()
            self.parser.exit(2, "error: Missing COMMAND\n")

        self.options.cmd()


def main() -> None:
    """CLI entry point."""
    SpeedCLI().main()


if __name__ == "__main__":
    main()
