#!/usr/bin/python3
"""Test the operation of the tinydns utilities."""

import argparse
import dataclasses
import errno
import os
import pathlib
import sys
import tempfile

from . import defs
from . import test_conf
from . import test_data
from . import test_edit
from . import test_get
from . import test_run


@dataclasses.dataclass(frozen=True)
class Config(defs.Config):
    """Runtime configuration for the test runner."""

    skip_run_test: bool


def parse_arguments() -> Config:
    """Parse the command-line arguments."""
    parser = argparse.ArgumentParser(prog="tinytest")
    parser.add_argument(
        "-b",
        "--bindir",
        type=pathlib.Path,
        required=True,
        help="the path to the djbdns user tools",
    )
    parser.add_argument(
        "-s",
        "--sbindir",
        type=pathlib.Path,
        required=True,
        help="the path to the djbdns system tools",
    )
    parser.add_argument(
        "-S",
        "--skip-run-test",
        action="store_true",
        help="skip the test that runs tinydns",
    )
    parser.add_argument(
        "-v",
        "--verbose",
        action="store_true",
        help="verbose operation; display diagnostic output",
    )

    args = parser.parse_args()

    subenv = dict(os.environ)
    subenv["LC_ALL"] = "C"
    subenv["LANGUAGE"] = "en"

    return Config(
        bindir=args.bindir,
        sbindir=args.sbindir,
        skip_run_test=args.skip_run_test,
        subenv=subenv,
        verbose=args.verbose,
    )


def run_tests(cfg: Config, tempd: pathlib.Path) -> None:
    """Run some tests."""
    cfg.diag(f"Running tests in {tempd}")
    os.chdir(tempd)

    test_edit.test_tinydns_edit(cfg, tempd)
    test_data.test_tinydns_data(cfg, tempd)
    test_get.test_tinydns_get(cfg, tempd)
    test_conf.test_tinydns_conf(cfg, tempd)

    if not cfg.skip_run_test:
        test_run.test_tinydns_run(cfg, tempd)
        test_run.test_tinydns_run_udp(cfg, tempd)
    else:
        print("\n==== Skipping test_tinydns_run*")

    print("\n==== All fine!")


def fixup_tempd_group(cfg: Config, tempd: pathlib.Path) -> None:
    """Change a temporary directory's group if needed."""
    testdir = tempd / "test-perms"
    testdir.mkdir()
    cfg.diag(f"Trying to set access mode 3755 on {testdir}")
    failed = False
    try:
        testdir.chmod(0o3755)
    except OSError as err:
        failed = True
        cfg.diag(f"- failed: {err}")
        if err.errno not in (errno.EPERM, errno.EACCES):
            raise

    if not failed:
        cfg.diag("Everything seems to be in order")
        return

    cfg.diag(f"Removing {testdir}")
    testdir.rmdir()

    cfg.diag("Examining the situation")
    tempd_stat = tempd.stat()
    tempd_gid = tempd_stat.st_gid
    our_gid = os.getgid()
    cfg.diag(f"- tempd gid: {tempd_gid} our gid: {our_gid}")
    if tempd_gid == our_gid:
        sys.exit("Don't know how to handle things with the same group ID")
    cfg.diag(
        f"Trying to change the group ID of {tempd} "
        f"from {tempd_gid} to {our_gid}"
    )
    os.chown(tempd, tempd_stat.st_uid, our_gid)

    cfg.diag("Now retrying the test")
    testdir.mkdir()
    testdir.chmod(0o3755)
    testdir.rmdir()
    cfg.diag(f"Looks like we fixed the access permissions of {tempd}")


def main() -> None:
    """Main program: parse options, run the tests."""
    cfg = parse_arguments()
    with tempfile.TemporaryDirectory() as tempd_obj:
        tempd = pathlib.Path(tempd_obj).absolute()
        cfg.diag(f"Using temporary directory {tempd}")
        fixup_tempd_group(cfg, tempd)
        try:
            run_tests(cfg, tempd)
        finally:
            try:
                os.chdir("/")
            except OSError as err:
                print(f"Could not chdir to /: {err}", file=sys.stderr)


if __name__ == "__main__":
    main()
