#!/usr/bin/env python3
# -*- coding: utf-8; py-indent-offset: 4 -*-
#
# Author:  Linuxfabrik GmbH, Zurich, Switzerland
# Contact: info (at) linuxfabrik (dot) ch
#          https://www.linuxfabrik.ch/
# License: The Unlicense, see LICENSE file.

# https://github.com/Linuxfabrik/monitoring-plugins/blob/main/CONTRIBUTING.md

"""See the check's README for more details."""

import argparse
import json
import sys

import lib.args
import lib.base
import lib.human
import lib.lftest
from lib.globals import STATE_OK, STATE_UNKNOWN

try:
    import psutil
except ImportError:
    print('Python module "psutil" is not installed.')
    sys.exit(STATE_UNKNOWN)

# psutil 5.x exposes sbattery under psutil._common, psutil 6+ under
# psutil._ntuples. Fall back to a plain namedtuple if neither is
# available.
try:
    from psutil._ntuples import sbattery
except ImportError:
    try:
        from psutil._common import sbattery
    except ImportError:
        from collections import namedtuple
        sbattery = namedtuple('sbattery', ['percent', 'secsleft', 'power_plugged'])


__author__ = 'Linuxfabrik GmbH, Zurich/Switzerland'
__version__ = '2026041301'

DESCRIPTION = """Reports battery status information including charge percentage, time remaining, and
power source (AC or battery). Returns OK if no battery is installed or if metrics
cannot be determined."""

DEFAULT_WARN = 20  # %
DEFAULT_CRIT = 5  # %


def parse_args():
    """Parse command line arguments using argparse."""
    parser = argparse.ArgumentParser(description=DESCRIPTION)

    parser.add_argument(
        '-V',
        '--version',
        action='version',
        version=f'%(prog)s: v{__version__} by {__author__}',
    )

    parser.add_argument(
        '--always-ok',
        help=lib.args.help('--always-ok'),
        dest='ALWAYS_OK',
        action='store_true',
        default=False,
    )

    parser.add_argument(
        '-c',
        '--critical',
        help='CRIT threshold for battery power left, in percent. Default: %(default)s',
        dest='CRIT',
        type=int,
        default=DEFAULT_CRIT,
    )

    parser.add_argument(
        '--test',
        help=lib.args.help('--test'),
        dest='TEST',
        type=lib.args.csv,
    )

    parser.add_argument(
        '-w',
        '--warning',
        help='WARN threshold for battery power left, in percent. Default: %(default)s',
        dest='WARN',
        type=int,
        default=DEFAULT_WARN,
    )

    args, _ = parser.parse_known_args()
    return args


def _load_battery_fixture(raw_json):
    """Convert a test fixture into the shape that `psutil.sensors_battery()`
    returns: an `sbattery(percent, secsleft, power_plugged)` namedtuple,
    or `None` if the fixture represents "no battery installed".
    """
    data = json.loads(raw_json)
    if data is None:
        return None
    return sbattery(
        percent=data['percent'],
        secsleft=data.get('secsleft', -2),  # psutil.POWER_TIME_UNLIMITED == -2
        power_plugged=data.get('power_plugged'),
    )


def main():
    """The main function. This is where the magic happens."""

    # parse the command line
    try:
        args = parse_args()
    except SystemExit:
        sys.exit(STATE_UNKNOWN)

    if args.TEST is None and not hasattr(psutil, 'sensors_battery'):
        lib.base.cu('Platform not supported.')

    # fetch data
    if args.TEST is None:
        batt = psutil.sensors_battery()
    else:
        stdout, _, _ = lib.lftest.test(args.TEST)
        batt = _load_battery_fixture(stdout)
    if batt is None:
        lib.base.oao('No battery installed.', STATE_OK, always_ok=args.ALWAYS_OK)

    # build the message
    msg = f'{round(batt.percent, 2)}%'
    if batt.power_plugged:
        msg += (
            f' (plugged in and {"charging" if batt.percent < 100 else "fully charged"})'
        )
    else:
        msg += f', {lib.human.seconds2human(batt.secsleft)} left '
        msg += '(not plugged in and discharging)'

    perfdata = ''
    perfdata += lib.base.get_perfdata(
        'battery_percent',
        batt.percent,
        uom='%',
        warn=args.WARN,
        crit=args.CRIT,
        _min=0,
        _max=100,
    )
    perfdata += lib.base.get_perfdata(
        'battery_secsleft',
        batt.secsleft,
        uom='s',
        _min=0,
    )
    state = lib.base.get_state(batt.percent, args.WARN, args.CRIT, 'le')
    msg += lib.base.state2str(state, prefix=' ')

    # over and out
    lib.base.oao(msg, state, perfdata, always_ok=args.ALWAYS_OK)


if __name__ == '__main__':
    try:
        main()
    except Exception:
        lib.base.cu()
