#! /usr/bin/python3
#
# This file is part of turnkey-version
#
# turnkey-version is open source software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 3 of the
# License, or (at your option) any later version.
"""Displays TurnKey GNU/Linux version information.

Also provides the turnkey sysversion python library used by the MOTD generaton
script to show basic system info.
"""

import argparse
import json
import os
import sys
from os.path import isfile, join
from typing import NoReturn

import sysversion

_ROOTFS = "/"
_TKL_VER = "etc/turnkey_version"


def fatal(msg: str) -> NoReturn:
    print(f"Error: {msg}", file=sys.stderr)
    sys.exit(1)


def main() -> None:
    parser = argparse.ArgumentParser(
            description="TurnKey Linux Version information",
    )
    file_or_str = parser.add_mutually_exclusive_group()
    # rootfs provides backwards compatability
    file_or_str.add_argument(
        "rootfs",
        type=str,
        nargs="?",
        default=_ROOTFS,
        help="Path to root filesystem, defaults to '/'. Will read file"
        f" 'ROOTFS/{_TKL_VER}'.",
    )
    file_or_str.add_argument(
        "-f",
        "--file",
        type=str,
        metavar="PATH_TO_FILE",
        default=None,
        help="Absolute (i.e. leading /) or relative (to current; i.e. no"
        " leading /) path to file containing turnkey version string. NOTE:"
        " conflicts with -s|--string.",
    )
    file_or_str.add_argument(
        "-s",
        "--string",
        type=str,
        metavar="TKL_VER_STR",
        help="Process a string, instead of reading from a file. ('turnkey-'"
        " prefix optional). NOTE: Conflicts with -f|--file.",
    )
    output = parser.add_argument_group(
        "Output options",
        description="Return space separated values, or all values in json"
        " format. NOTE: If multiple elements returned, the order will always"
        " be: 'NAME VERSION CODENAME ARCH' (regardless of switch order).",
    )
    output.add_argument(
        "-n",
        "--name",
        action="store_true",
        help="Return TurnKey appliance name. E.g. core.",
    )
    output.add_argument(
        "-t",
        "--tklversion",
        action="store_true",
        help="Return TurnKey version number. E.g. 19.0.",
    )
    output.add_argument(
        "-c",
        "--codename",
        action="store_true",
        help="Return relevant Debian codename. E.g. trixie.",
    )
    output.add_argument(
        "-r",
        "--arch",
        action="store_true",
        help="Return relevant architechture. E.g. amd64",
    )
    output.add_argument(
        "-a",
        "--all",
        action="store_true",
        help="Return space separated name, version, codename & arch. (Same as"
        " -ntcr).",
    )
    output.add_argument(
        "-j",
        "--json",
        action="store_true",
        help="Return all name info in json format (conflicts with other output"
        " options).",
    )
    additional_output = parser.add_argument_group(
        "Additional output options",
        description="Return additional values. If no other output options"
        " specified, these will be the only output. Otherwise, these values"
        " will be appended after any other results returned, either space"
        " separated, or if -j|--json, as additional key, value pairs.",
    )
    additional_output.add_argument(
        "-d",
        "--debian-codename",
        action="store_true",
        help="Return system's reported Debian codename (not TurnKey version"
        " codename). If rootfs set, then return codename of chroot.",
    )

    args = parser.parse_args()
    if args.name or args.tklversion or args.codename or args.arch:
        output_values = True
    else:
        output_values = False
    if args.all and output_values:
        parser.error(
            "-a|--all and -n|--name / -t|--tklversion / -c|--codename"
            " / -r|--arch are mutually exclusive.",
        )
    if args.json and (args.all or output_values):
        parser.error("-j|--json conflicts with other output values.")
    if args.string:
        file = ""
        turnkey_version = args.string
        rootfs = ""
    else:
        # ensure that args.file is relative to args.rooffs
        if args.file:
            rootfs = ""
            v_file = os.path.abspath(args.file)
        else:
            rootfs = args.rootfs
            v_file = _TKL_VER
        file = join(rootfs, v_file)
        turnkey_version = sysversion.get_turnkey_version(rootfs, v_file)
    if not turnkey_version:
        msg = "can't detect turnkey version"
        if file and not isfile(file):
            msg = f"{msg} - missing/invalid file: '{file}'"
        else:
            msg = f"{msg} - invalid version string"
        fatal(msg)

    tkl_appver = []
    tkl_appver_cls = sysversion.AppVer(turnkey_version, rootfs=rootfs)
    if args.all:
        args.name = True
        args.tklversion = True
        args.codename = True
        args.arch = True
    if args.name:
        tkl_appver.append(tkl_appver_cls.appname)
    if args.tklversion:
        tkl_appver.append(tkl_appver_cls.tklver)
    if args.codename:
        tkl_appver.append(tkl_appver_cls.codename)
    if args.arch:
        tkl_appver.append(tkl_appver_cls.arch)
    if args.debian_codename:
        tkl_appver.append(tkl_appver_cls.deb_codename)
    if args.json:
        print(json.dumps(tkl_appver_cls.app_json(
              deb_ver=args.debian_codename), indent=2))
    elif tkl_appver:
        print(" ".join(tkl_appver))
    else:
        print(turnkey_version)


if __name__ == "__main__":
    main()
