'''Contains helper methods for writing to and reading from
the console that can be configured by arguments, such as
verbose logging and dryrunning potentially dangerous commands.
'''

import collections
import os
import subprocess


LOG_LEVEL = collections.namedtuple('LL', ['NORM', 'VERBOSE', 'DEBUG'])(1, 2, 3)
SET_LOG_LEVEL = LOG_LEVEL.NORM


EXEC_MOD = collections.namedtuple('EM', ['NORM', 'DRY'])
SET_EXEC_MOD = EXEC_MOD.NORM


def log(message, verbosity=LOG_LEVEL.NORM, **kwargs):
  if verbosity >= SET_LOG_LEVEL:
    print(f'[ {message} ]', **kwargs)


def run(command, **kwargs):
  if SET_EXEC_MOD == EXEC_MOD.NORM:
    return subprocess.run(command,
                          encoding=kwargs.get('encoding', 'utf-8'),
                          shell=kwargs.get('shell', False),
                          stderr=kwargs.get('stderr', subprocess.PIPE),
                          stdout=kwargs.get('stdout', subprocess.PIPE))
  else:
    # Print it, not 'echo' it, since there might be some funky unescape stuff
    print(' '.join(command))
    return subprocess.CompletedProcess(['echo'] + command, 0)


def output_or_error(command, error_gen=None, **kwargs):
  ''' Run a command and return the output as a string, or raise error.
  error_gen: a function that could either return or raise an error
             from a given subprocess.CompletedProcess object.
  '''
  result = run(command, **kwargs)
  if result.returncode:
    if error_gen is not None:
      raise error_gen(result)
    raise RuntimeError(f'`{" ".join(command)}` ({result.returncode})\n\t{result.stderr}\n\t{result.stdout}')

  if kwargs.get('stdout', subprocess.PIPE) == subprocess.PIPE:
    return result.stdout.strip()
  return ''


def check_run(command, error_gen=None, **kwargs):
  ''' Just like output_or_error, but returns None on success.
  '''
  output_or_error(command, error_gen, **kwargs)
  return None


def stdout_fail_ok(command, **kwargs):
  return run(command, **kwargs).stdout.strip()
