#!/usr/bin/python

import argparse
import glob
from distutils.dir_util import mkpath as mkdir_p
import os
import shutil
import string
import sys

sys.path.append('/usr/share/l.v.e-manager/cpanel/lib')
from extensions import get_user_path, user_check


TOOL_LIST_GLOB = '/usr/share/examine-lsphp/*-lsphp.sh'


class OopsError (RuntimeError): pass


class Tool (object):

    def __init__(self, path):
        self._path = path

    def name(self):
        return os.path.split(self._path)[1].split('-')[0]

    def path(self):
        return self._path


class ToolList (object):

    def __init__(self):
        self._list = [Tool(path) for path in glob.glob(TOOL_LIST_GLOB)]

    def __str__(self):
        name_list = [x.name() for x in self._list]
        if len(name_list) < 3:
            return ' or '.join(name_list)
        elif len(name_list) < 5:
            return "%s or %s" % (', '.join(name_list[:-1]), name_list[-1])
        else:
            return 'available tools'

    def __contains__(self, item):
        return any(x.path() == item for x in self._list)

    def __iter__(self):
        return iter(self._list)

    def lookup_tool_path(self, tool_name):
        found = [x for x in self._list if x.name() == tool_name]
        if not found:
            raise OopsError("Invalid argument: " + tool_name)
        result, = found
        return result.path()


def get_lsphp_selector_path(username):
    user_path = get_user_path(username)
    if user_path is None:
        raise OopsError("OopsError user '%s'" % username)
    return os.path.join(user_path, 'lsphp')


def get_lsphp_selector_backup_path(username):
    cagefs_user_path = user_check(username)
    if cagefs_user_path is None:
        raise OopsError("OopsError user '%s'" % username)
    selector_backup_path = os.path.join(
        '/var/cagefs',
        cagefs_user_path,
        username,
        'etc',
        'examine-lsphp',
        'cl.selector.lsphp'
    )
    return selector_backup_path


def _symlink(source, link_name):
    print source, '->', link_name
    try:
        os.unlink(link_name)
    except OSError:
        pass
    os.symlink(source, link_name)


def set_user_current(username, tool_name):

    lsphp_selector_path = get_lsphp_selector_path(username)
    tool_path = g_tool_list.lookup_tool_path(tool_name)
    if os.readlink(lsphp_selector_path) == tool_path:
        print lsphp_selector_path, "is already symlinked to", tool_path
        return 1

    # create backup link
    source, link_name = os.readlink(lsphp_selector_path), get_lsphp_selector_backup_path(username)
    if not source in g_tool_list:
        mkdir_p(os.path.dirname(link_name))
        _symlink(source, link_name)

    source, link_name = tool_path, lsphp_selector_path
    _symlink(source, link_name)

    return 0


def revert_user(username, verbose):

    lsphp_selector_backup_path = get_lsphp_selector_backup_path(username)
    if not os.path.islink(lsphp_selector_backup_path):
        if verbose:
            print "File %s does not exist or is not a symlink" % get_lsphp_selector_backup_path(username)
            print "Nothing to do."
        return 1

    # restore backup link
    source, link_name = os.readlink(lsphp_selector_backup_path), get_lsphp_selector_path(username)
    _symlink(source, link_name)
    os.unlink(lsphp_selector_backup_path)

    return 0


def purge_user(username):

    revert_user(username, verbose=False)

    log_dir = os.path.join(os.path.expanduser("~%s/.examine-lsphp" % username))
    if os.path.isdir(log_dir):
        print "rm -rf", log_dir
        shutil.rmtree(log_dir)

    return 0


def all_users_do(func, **kwarg):

    for path in glob.glob("/var/cagefs/*/*/etc/examine-lsphp"):
        username = string.split(path, '/')[4]
        try:
            func(username, **kwarg)
        except OSError as e:
            print >>sys.stderr, "User '%s': %s" % (username, e)

    return 0


def show_user_details(username):

    lsphp_selector_path = get_lsphp_selector_path(username)
    lsphp_selector_backup_path = get_lsphp_selector_backup_path(username)

    tool_path = os.readlink(lsphp_selector_path)
    if tool_path in g_tool_list:
        print "User:", username
        print "  %s -> %s" % (lsphp_selector_path, tool_path)
        print "  %s -> %s" % (tool_path, os.readlink(lsphp_selector_backup_path))
        return True
    else:
        return False


def show_details_all_users():

    # trick to workaround python 2.7 limits for closure functions
    class ScopeTrick (object): pass
    _ = ScopeTrick()
    _.count = 0

    def func(user):
        if show_user_details(user):
            _.count += 1
        return 0

    all_users_do(func)
    if not _.count:
        print "No users have lsphp linked to %s." % g_tool_list

    return 0


def list_tools():
    for tool in g_tool_list:
        print "%s: %s" % (tool.name(), tool.path())
    return 0


def main():

    global g_tool_list
    g_tool_list = ToolList()

    parser = argparse.ArgumentParser()
    parser.add_argument('--set-user-current', nargs=2, metavar=('username',
                                                                'tool'),
                        help="substitute user's lsphp alternative with %s symlink"
                            % g_tool_list)
    parser.add_argument('--revert-user', metavar="username",
                        help="revert user alternative to the previous state")
    parser.add_argument('--list', action='store_true',
                        help="list users have lsphp symlinked to %s"
                            % g_tool_list)
    parser.add_argument('--list-tools', action='store_true',
                        help="list tools available")
    parser.add_argument('--revert-all', action='store_true',
                        help="revert back all users")
    parser.add_argument('--purge-all', action='store_true',
                        help="revert and purge logs")
    args = parser.parse_args()
    if args.set_user_current:
        return set_user_current(*args.set_user_current)
    elif args.revert_user:
        return revert_user(args.revert_user, verbose=True)
    elif args.revert_all:
        return all_users_do(revert_user, verbose=False)
    elif args.list:
        return show_details_all_users()
    elif args.list_tools:
        return list_tools()
    elif args.purge_all:
        return all_users_do(purge_user)
    else:
        parser.print_help()  # sys.exit() is called by parser.print_help() implicitly


if __name__ == "__main__":
    try:
        err = main()
    except OopsError as e:
        print >>sys.stderr, e
        err = 1
    if not err:
        print "Done"
    sys.exit(err)
