# Copyright (C) 2013 Kristoffer Gronlund <kgronlund@suse.com>
# Copyright (C) 2008-2011 Dejan Muhamedagic <dmuhamedagic@suse.de>
# See COPYING for license information.
import re
import shlex

from . import clidisplay
from . import utils
from .sh import ShellUtils

_crm_mon = None

_WARNS = ['pending',
          'complete',
          'Timed Out',
          'NOT SUPPORTED',
          'Error',
          'Not installed',
          r'UNKNOWN\!',
          'Stopped',
          'standby',
          'WITHOUT quorum']
_OKS = ['Started', 'Online', 'online', 'ok', 'with quorum', 'Promoted', 'Unpromoted']
_ERRORS = ['not running',
           'unknown error',
           'invalid parameter',
           'unimplemented feature',
           'insufficient privileges',
           'not installed',
           'not configured',
           'not running',
           'promoted (failed)',
           'OCF_SIGNAL',
           'OCF_NOT_SUPPORTED',
           'OCF_TIMEOUT',
           'OCF_OTHER_ERROR',
           'OCF_DEGRADED',
           'OCF_DEGRADED_PROMOTED',
           'unknown',
           'Unknown',
           'OFFLINE',
           'Failed actions']
CRM_MON_OPTIONS_MAP = {
        "bynode": "-n",
        "inactive": "-r",
        "ops": "-o",
        "timing": "-t",
        "failcounts": "-f",
        "verbose": "-V",
        "quiet": "-Q",
        "html": "--output-as html",
        "xml": "--output-as xml",
        "tickets": "-c",
        "noheaders": "-D",
        "detail": "-R",
        "brief": "-b",
        "full": "-ncrft",
}


class CrmMonFilter(object):
    _OK = re.compile(r'(%s)' % '|'.join(r"(?:\b%s\b)" % (w) for w in _OKS))
    _WARNS = re.compile(r'(%s)' % '|'.join(_WARNS))
    _ERROR = re.compile(r'(%s)' % ('|'.join(_ERRORS)))
    _NODES = re.compile(r'(\d+ Nodes configured)')
    _RESOURCES = re.compile(r'(\d+ Resources configured)')

    _RESOURCE = re.compile(r'(\S+)(\s+)\((\S+:\S+)\):')
    _GROUP = re.compile(r'((?:Resource Group)|(?:Clone Set)): (\S+)')

    def _filter(self, line):
        line = self._RESOURCE.sub("%s%s(%s):" % (clidisplay.help_header(r'\1'),
                                                 r'\2',
                                                 r'\3'), line)
        line = self._NODES.sub(clidisplay.help_header(r'\1'), line)
        line = self._RESOURCES.sub(clidisplay.help_header(r'\1'), line)
        line, ngroups = self._GROUP.subn(r'\1: ' + clidisplay.help_header(r'\2'), line)
        if ngroups == 0:
            line = self._WARNS.sub(clidisplay.warn(r'\1'), line)
            line = self._OK.sub(clidisplay.ok(r'\1'), line)
            line = self._ERROR.sub(clidisplay.error(r'\1'), line)
        return line

    def __call__(self, text):
        return '\n'.join([self._filter(line) for line in text.splitlines()]) + '\n'


def crm_mon(opts=''):
    """
    Run 'crm_mon -1'
    opts: Additional options to pass to crm_mon
    returns: rc, stdout
    """
    global _crm_mon
    shell = ShellUtils()
    if _crm_mon is None:
        prog = utils.is_program("crm_mon")
        if not prog:
            raise IOError("crm_mon not available, check your installation")
        _crm_mon = [prog, '-1']
        _, out = shell.get_stdout([prog, '--help'], shell=False)
        if "--pending" in out:
            _crm_mon.append('-j')

    return shell.get_stdout_stderr(_crm_mon + shlex.split(opts), shell=False)


def cmd_status(args):
    '''
    Calls crm_mon -1, passing optional extra arguments.
    Displays the output, paging if necessary.
    Raises IOError if crm_mon fails.
    '''
    extra = ' '.join(CRM_MON_OPTIONS_MAP.get(arg, arg) for arg in args)
    if not args:
        extra = "-r"
    rc, s, err = crm_mon(extra)
    if rc != 0:
        raise IOError(f"{err} (rc={rc})")

    utils.page_string(CrmMonFilter()(s))
    return True


def cmd_verify(args):
    '''
    Calls crm_verify -LV; ptest -L -VVVV
    '''
    from . import config
    if "ptest" in config.core.ptest:
        cmd1 = "crm_verify -LVVV; %s -L -VVVV" % (config.core.ptest)
    else:
        cmd1 = "crm_verify -LVVV; %s -LjV" % (config.core.ptest)

        if "scores" in args:
            cmd1 += " -s"

    cmd1 = utils.add_sudo(cmd1)
    rc, s, e = ShellUtils().get_stdout_stderr(cmd1)
    e = '\n'.join(clidisplay.error(l) for l in e.split('\n')).strip()
    utils.page_string("\n".join((s, e)))
    return rc == 0 and not e
