#!/usr/bin/env python3

"""
check_npm_updates

Monitoring plugin to check available updates for an npm-installed package
"""

import json
import os
import subprocess
import sys
from argparse import ArgumentParser
from enum import IntEnum

from packaging.version import Version


def main():

    args = parse_args()
    if args.directory:
        directory = os.path.abspath(os.path.expanduser(args.directory))
    else:
        directory = None

    installed = npm_ls(args.package, args.is_global, directory)
    if not installed or not args.package in installed["dependencies"]:
        print(
            f"UNKNOWN - {args.package} wasn't found, is it installed?", file=sys.stderr
        )
        sys.exit(ExitCode.UNKNOWN)

    outdated = npm_outdated(args.package, args.is_global, directory)

    if not outdated:
        print(f"OK - No updates available for {args.package}.")
        sys.exit(ExitCode.OK)

    current = Version(outdated[args.package]["current"])
    wanted = Version(outdated[args.package]["wanted"])
    if current < wanted:
        print(f"WARNING - Available update for {args.package}: {current} -> {wanted}")
        sys.exit(ExitCode.WARNING)
    print(
        f"UNKNOWN - Error comparing versions for {args.package}: {current} / {wanted}"
    )


def npm_ls(package: str, is_global: bool, directory: str | None) -> dict:
    """Run `npm ls` on given package"""

    cmd = ["npm", "ls", "--json", package]
    if is_global:
        cmd.append("--global")
    result = subprocess.run(
        cmd,
        cwd=directory,
        capture_output=True,
        check=False,
    )

    return json.loads(result.stdout.decode())


def npm_outdated(package: str, is_global: bool, directory: str | None) -> dict:
    """Run `npm outdated` on given package"""

    cmd = ["npm", "outdated", "--json", package]
    if is_global:
        cmd.append("--global")
    result = subprocess.run(
        cmd,
        cwd=directory,
        capture_output=True,
        check=False,
    )

    return json.loads(result.stdout.decode())


class ExitCode(IntEnum):
    """Enum for exit codes"""

    OK = 0
    WARNING = 1
    CRITICAL = 2
    UNKNOWN = 3


def parse_args():
    parser = ArgumentParser(
        description="Monitoring plugin to check available updates for an npm-installed package."
    )

    parser.add_argument("package", help="Name of the package to monitor updates for.")
    parser.add_argument(
        "-g",
        "--global",
        action="store_true",
        dest="is_global",
        help="Use global npm",
    )
    parser.add_argument(
        "-d",
        "--directory",
        help="Optional path to directory containing the package install (node_modules)",
    )

    return parser.parse_args()


if __name__ == "__main__":
    main()
