#!/usr/bin/python3
# -*- mode:python; coding:utf-8; -*-
import getopt
import glob
import logging
import os
import subprocess
import sys
from shutil import copy2
try:
    import db.clcommon.cpapi as cpapi
except ImportError:
    import detectcp as cpapi
MODES = ("check", "install", "uninstall")

def is_plesk():
    """
    Check is it environment with installed plesk panel

    @rtype  : bool
    @return  True or False
    """
    if not os.path.exists("/usr/sbin/plesk"):
        return False
    return True

def has_cagefs():
    """
    Check if we're in environment with enabled cagefs

    @rtype  : bool
    @return  True or False
    """
    if not os.path.exists("/usr/sbin/cagefsctl"):
        return False

    with open(os.devnull, 'wb') as devnull:
        result = subprocess.call(
            ["/usr/sbin/cagefsctl", "--cagefs-status"],
            stdout=devnull,
            stderr=devnull
        )
    return result == 0

def is_bare_plesk():
    """
    Check is it environment with installed plesk panel on clean ELS system without cagefs

    @rtype  : bool
    @return  True or False
    """
    return is_plesk() and not has_cagefs()

def configure_logging(verbose):
    """
    Logging configuration function

    :type verbose: bool
    :param verbose: Enable additional debug output if True, display only errors
        othervise
    :return: configured logger object
    """
    if verbose:
        level = logging.DEBUG
    else:
        level = logging.ERROR
    handler = logging.StreamHandler()
    handler.setLevel(level)
    log_format = "%(levelname)-8s: %(message)s"
    formatter = logging.Formatter(log_format, "%H:%M:%S %d.%m.%y")
    handler.setFormatter(formatter)
    logger = logging.getLogger()
    logger.addHandler(handler)
    logger.setLevel(level)
    return logger


def find_alt_php_versions():
    """
    Returns list of installed alt-php versions and their base directories

    :rtype:  list
    :return:  List of version (e.g. 44, 55) and base directory tuples
    """
    php_versions = []
    for php_dir in glob.glob("/opt/alt/php[0-9][0-9]"):
        php_versions.append((php_dir[-2:], php_dir))
    php_versions.sort()
    return php_versions


def plesk_check_php_handler(cgi_type, php_ver):
    """

    :param php_ver: alt-php version (e.g. 44, 55, 70)
    :return: If handler exist returns True, otherwise False
    """
    proc = subprocess.Popen(["/usr/local/psa/bin/php_handler", "--list"],
                            stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
                            universal_newlines=True)
    out, _ = proc.communicate()
    for line in out.split("\n"):
        if 'alt-php%s-%s' % (php_ver, cgi_type) in line.strip().split(" ")[0]:
            logging.info("Handler for alt-php%s-%s exist." % (php_ver, cgi_type))
            return True
    logging.info("Handler for alt-php%s-%s not exist." % (php_ver, cgi_type))
    return False


def plesk_add_php_handler(cgi_type, php_ver, php_path):
    if is_bare_plesk():
        logging.info("Skipping alt-php%s-%s on Plesk installations without CageFS." % (php_ver, cgi_type))
        return True
    if plesk_check_php_handler(cgi_type, php_ver):
        logging.info("Handler for alt-php%s-%s exist." % (php_ver, cgi_type))
        return False
    logging.info("Plesk: Installing alt-php%s-%s handler." % (php_ver, cgi_type))
    sys.stdout.write("Plesk: Installing alt-php{0}-{1} handler.".format(php_ver, cgi_type))
    # run /usr/local/psa/bin/php_handler --add -displayname alt-php-7.0.0 -path /opt/alt/php70/usr/bin/php-cgi
    # -phpini /opt/alt/php70/etc/php.ini -type fastcgi -id 666 -clipath /opt/alt/php70/usr/bin/php
    command = "/usr/local/psa/bin/php_handler"
    add_command = [
        command, '--add',
        '-displayname', 'alt-php%s-%s' % (php_ver, cgi_type),
        '-clipath', os.path.join(php_path, 'usr/bin/php'),
        '-phpini', os.path.join(php_path, 'etc/php.ini'),
        '-type', cgi_type,
        '-id', 'alt-php%s-%s' % (php_ver, cgi_type), ]
    if cgi_type == "fpm":
        add_command.extend([
        '-service', 'alt-php%s-fpm' % php_ver,
        '-path', os.path.join(php_path, 'usr/sbin/php-fpm'),
        '-poold', os.path.join(php_path, 'etc/php-fpm.d'),])
        if not os.path.exists("/opt/alt/php%s/etc/php-fpm.conf" % php_ver):
            copy2(os.path.join(php_path, 'etc/php-fpm.conf.plesk'), os.path.join(php_path, 'etc/php-fpm.conf'))
    else:
        add_command.extend([
        '-path', os.path.join(php_path, 'usr/bin/php-cgi'),])
    proc = subprocess.Popen(add_command, stdout=subprocess.PIPE,
                            stderr=subprocess.STDOUT, universal_newlines=True)
    out, _ = proc.communicate()
    if proc.returncode != 0:
        raise Exception(u"cannot execute \"%s\": %s" % (' '.join(add_command), out))
    proc = subprocess.Popen([command, "--reread"], stdout=subprocess.PIPE,
                            stderr=subprocess.STDOUT, universal_newlines=True)
    out, _ = proc.communicate()
    if proc.returncode != 0:
        raise Exception(u"cannot execute \"" + command + " --reread\": %s" % out)
    logging.info("Handler for alt-php%s was successfully added." % php_ver)
    return True


def plesk_remove_php_handler(cgi_type, php_ver):
    if plesk_check_php_handler(cgi_type, php_ver):
        logging.info("Plesk: Removing alt-php%s-%s handler." % (php_ver, cgi_type))
        sys.stdout.write("Plesk: Removing alt-php{0}-{1} handler.".format(php_ver, cgi_type))
        command = ["/usr/local/psa/bin/php_handler", "--remove",
                   "-id", "alt-php%s-%s" % (php_ver, cgi_type)]
        proc = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
                                universal_newlines=True)
        out, _ = proc.communicate()
        if proc.returncode != 0:
            raise Exception(u"cannot execute \"%s\": %s" % (' '.join(command), out))
        logging.info("Handler for alt-php%s-%s was successfully removed." % (php_ver, cgi_type))
        return True
    else:
        logging.info("Handler for alt-php%s-%s not exist." % (php_ver, cgi_type))
        return False


def configure_alt_php(mode, php_ver, php_path):
    """
    :rtype: bool
    :return: If success returns True, otherwise False
    """
    try:
        cp_name = cpapi.getCPName()
        if cp_name == "Plesk":
            if not os.path.exists("/usr/local/psa/bin/php_handler"):
                raise Exception("/usr/local/psa/bin/php_handler not exist.")
            if mode == "install":
                plesk_add_php_handler('fastcgi', php_ver, php_path)
                plesk_add_php_handler('cgi', php_ver, php_path)
                if os.path.exists("/etc/init.d/alt-php%s-fpm" % php_ver) or os.path.exists("/usr/lib/systemd/system/alt-php%s-fpm.service" % php_ver):
                    plesk_add_php_handler('fpm', php_ver, php_path)
            elif mode == "uninstall":
                plesk_remove_php_handler('fastcgi', php_ver)
                plesk_remove_php_handler('cgi', php_ver)
                if os.path.exists("/etc/init.d/alt-php%s-fpm" % php_ver) or os.path.exists("/usr/lib/systemd/system/alt-php%s-fpm.service" % php_ver):
                    plesk_remove_php_handler('fpm', php_ver)
            else:
                return plesk_check_php_handler('fastcgi', php_ver) and plesk_check_php_handler('cgi', php_ver) and plesk_check_php_handler('fpm', php_ver)
    except Exception as e:
        logging.info(e)
        return False


def main(sys_args):
    try:
        opts, args = getopt.getopt(sys_args, "m:p:v", ["mode=", "php=", "verbose"])
    except getopt.GetoptError as e:
        sys.stderr.write("cannot parse command line arguments: {0}".format(e))
        return 1
    verbose = False
    mode = "check"
    php_versions = []
    for opt, arg in opts:
        if opt in ("-m", "--mode"):
            if arg not in MODES:
                # use check mode
                mode = "check"
            else:
                mode = arg
        if opt in ("-p", "--php"):
            if not os.path.isdir("/opt/alt/php%s" % arg):
                sys.stderr.write("unknown PHP version {0}".format(arg))
                return 1
            php_versions.append((arg, "/opt/alt/php%s" % arg))
        if opt in ("-v", "--verbose"):
            verbose = True
    log = configure_logging(verbose)

    if not php_versions:
        php_versions = find_alt_php_versions()
        log.info(u"installed alt-php versions are\n%s" %
                 "\n".join(["\t alt-php%s: %s" % i for i in php_versions]))
    for ver, path in php_versions:
        configure_alt_php(mode, ver, path)


if __name__ == "__main__":
    sys.exit(main(sys.argv[1:]))
