# (c) 2026 Applied Brain Research
#
# All information contained herein is and remains the property of Applied Brain
# Research. The intellectual and technical concepts contained herein are
# proprietary to Applied Brain Research and may be covered by U.S. and Foreign
# Patents, patents in process, and are protected by trade secret or copyright
# law. Dissemination of this information or reproduction of this material is
# strictly forbidden unless prior written permission is obtained from Applied
# Brain Research. Access to the source code contained herein is hereby
# forbidden to anyone except current Applied Brain Research employees,
# contractors or other outside parties that have executed Confidentiality
# and/or Non-disclosure agreements explicitly covering such access.
#
# The copyright notice above does not evidence any actual or intended
# publication or disclosure of this source code, which includes information
# that is confidential and/or proprietary, and is a trade secret, of Applied
# Brain Research. ANY REPRODUCTION, MODIFICATION, DISTRIBUTION, PUBLIC
# PERFORMANCE, OR PUBLIC DISPLAY OF OR THROUGH USE OF THIS SOURCE CODE WITHOUT
# THE EXPRESS WRITTEN CONSENT OF APPLIED BRAIN RESEARCH IS STRICTLY PROHIBITED,
# AND IN VIOLATION OF APPLICABLE LAWS AND INTERNATIONAL TREATIES. THE RECEIPT
# OR POSSESSION OF THIS SOURCE CODE AND/OR RELATED INFORMATION DOES NOT CONVEY
# OR IMPLY ANY RIGHTS TO REPRODUCE, DISCLOSE OR DISTRIBUTE ITS CONTENTS, OR TO
# MANUFACTURE, USE, OR SELL ANYTHING THAT IT MAY DESCRIBE, IN WHOLE OR IN PART.

"""
The ``abr-sdk`` command-line tool.

Currently exposes one subcommand, ``activate``, which reads this device's
fingerprint from the SDK library, trades a license key for a signed machine
file from Keygen, and writes it under the license directory.

The fingerprint is read from the library's ``fingerprint`` property, available
without instantiating a model, so activation needs no license to discover the
fingerprint it must bind to. Verifying and installing the downloaded envelope is
the SDK's job: ``abr_sdk.core.install_license`` hands it to the native
``abr_app_activate_license``, which checks the signature and writes the
device-bound license and integrity files under the license directory.

Author: Trevor Bekolay (Applied Brain Research)
"""

from __future__ import annotations

import argparse
import enum
import string
import sys
from pathlib import Path
from typing import Final

from abr_sdk.cabi import AbrSdkCAbiError, Status
from abr_sdk.core import DEFAULT_LICENSE_DIR, Library, install_license
from abr_sdk.exceptions import AbrSdkError
from abr_sdk.keygen import ActivationError, Failure, KeygenClient


class ExitCode(enum.IntEnum):
    """Process exit codes. The specific reason is in the stderr message."""

    SUCCESS = 0
    ERROR = 1
    NETWORK = 2
    LICENSE = 3


_EXIT_FOR_FAILURE: Final[dict[Failure, ExitCode]] = {
    Failure.NETWORK: ExitCode.NETWORK,
    Failure.LICENSE: ExitCode.LICENSE,
    Failure.PROTOCOL: ExitCode.ERROR,
}


def build_parser() -> argparse.ArgumentParser:
    """Construct the top-level ``abr-sdk`` parser with its subcommands."""
    parser = argparse.ArgumentParser(
        prog="abr-sdk", description="ABR SDK command-line tools."
    )
    sub = parser.add_subparsers(dest="command", required=True)

    activate = sub.add_parser(
        "activate", help="Activate this device's ABR license against Keygen."
    )
    activate.add_argument(
        "library", type=Path, help="Path to the ABR SDK shared library (.so)."
    )
    key_group = activate.add_mutually_exclusive_group(required=True)
    key_group.add_argument(
        "--key", metavar="STRING", help="The ABR license key, as a string."
    )
    key_group.add_argument(
        "--key-file",
        type=Path,
        metavar="FILE",
        help="Path to a file containing the ABR license key (whitespace trimmed).",
    )
    activate.add_argument(
        "--license-dir",
        type=Path,
        default=DEFAULT_LICENSE_DIR,
        metavar="DIR",
        help=f"Directory to write the license into (default: {DEFAULT_LICENSE_DIR}).",
    )
    activate.set_defaults(func=cmd_activate)
    return parser


def is_valid_fingerprint(value: str) -> bool:
    """Return True when *value* is exactly 64 hexadecimal characters."""
    return len(value) == 64 and all(c in string.hexdigits for c in value)


def cmd_activate(args: argparse.Namespace) -> int:
    """Read the device fingerprint from the library, then run activation."""
    try:
        key = _resolve_key(args)
    except OSError as exc:
        return _fail(ExitCode.ERROR, f"cannot read the key file {args.key_file}: {exc}")

    try:
        library = Library(args.library)
    except (AbrSdkError, OSError) as exc:
        return _fail(ExitCode.ERROR, f"cannot load the SDK library: {exc}")
    try:
        fingerprint = library.get_property("fingerprint")
    except KeyError:
        return _fail(ExitCode.ERROR, "SDK library exposes no 'fingerprint' property.")
    if not isinstance(fingerprint, str) or not is_valid_fingerprint(fingerprint):
        return _fail(ExitCode.ERROR, "device fingerprint from the library is invalid.")

    client = KeygenClient(key)
    return run_activation(client, library, fingerprint.lower(), args.license_dir)


def _resolve_key(args: argparse.Namespace) -> str:
    """Return the license key from ``--key`` or the ``--key-file`` contents."""
    if args.key_file is not None:
        return args.key_file.read_text(encoding="utf-8").strip()
    return args.key


def run_activation(
    client: KeygenClient, library: Library, fingerprint: str, license_dir: Path
) -> int:
    """Download the license envelope from Keygen and install it via the SDK."""
    try:
        envelope = client.activate(fingerprint)
    except ActivationError as exc:
        return _fail(_EXIT_FOR_FAILURE[exc.kind], str(exc))

    try:
        install_license(library, envelope, license_dir=license_dir)
    except AbrSdkCAbiError as exc:
        code = ExitCode.LICENSE if exc.status == Status.ERR_LICENSE else ExitCode.ERROR
        return _fail(code, f"could not install the license: {exc}")
    except AbrSdkError as exc:
        return _fail(ExitCode.ERROR, f"could not install the license: {exc}")

    print(f"abr-sdk: activation succeeded; license installed in {license_dir}")
    return ExitCode.SUCCESS


def _fail(code: ExitCode, message: str) -> int:
    """Print a prefixed message to stderr and return *code*."""
    print(f"abr-sdk: {message}", file=sys.stderr)
    return code


def main(argv: list[str] | None = None) -> None:
    """Parse arguments and dispatch to the selected subcommand."""
    args = build_parser().parse_args(argv)
    raise SystemExit(args.func(args))


if __name__ == "__main__":
    main()
