#!/usr/bin/python
# -*- coding: utf-8 -*-
import glob
import os
import pwd
import subprocess
import sys
from ConfigParser import ConfigParser, NoOptionError, NoSectionError


__cpname__ = 'cPanel'

DBMAPPING_SCRIPT = os.path.join(os.path.dirname(sys.executable), "cpanel-dbmapping")


def detect():
    return os.path.isfile('/usr/local/cpanel/cpanel')


from clcommon.clconfpars import load as loadconfig
#from .universal import _dblogin_cplogin_pairs
from clcommon.cpapi.cpapiexceptions import NoDBAccessData, CpApiTypeError, NoDomain
from clcommon import ClPwd

CPANEL_DB_CONF = '/root/.my.cnf'
CPANEL_USERPLANS_PATH = '/etc/userplans'
CPANEL_DATABASES_PATH = '/var/cpanel/databases/'
CPANEL_USERS_DIR = '/var/cpanel/users/'
CPANEL_RESELLERS_PATH = '/var/cpanel/resellers'
CPANEL_USERDATADOMAINS_PATH = '/etc/userdatadomains;/var/cpanel/userdata/{user}/cache'
CPANEL_ACCT_CONF_PATH = '/etc/wwwacct.conf'
SYSCONF_CLOUDLINUX_PATH = '/etc/sysconfig/cloudlinux'

USERCONF_PARAM_MAP = {
    'dns': 'dns',
    'package': 'plan',
    'reseller': 'owner',
    'mail': 'contactemail',
    'locale': 'locale',
    'cplogin': 'user'
}
SUPPORTED_CPANEL_CPINFO = ('cplogin', 'package', 'mail', 'reseller', 'dns', 'locale')


def db_access(_conf_path=CPANEL_DB_CONF):
    access = dict()
    reg_data_config = ConfigParser()
    opened_files = reg_data_config.read(_conf_path)
    if not opened_files:
        raise NoDBAccessData('Can not find database access data for localhost. No such file %s' % _conf_path)
    try:
        if reg_data_config.has_option(section='client', option='password'):
            access['pass'] = reg_data_config.get(section='client', option='password', raw=True).strip('\"')
        else:
            access['pass'] = reg_data_config.get(section='client', option='pass', raw=True).strip('\"')
        access['login'] = reg_data_config.get(section='client', option='user', raw=True)
    except (NoOptionError, NoSectionError), e:
        raise NoDBAccessData('Can not find database access data for localhost from config file %s; '
                             '%s' % (_conf_path, e.message))
    access['db'] = 'mysql'
    return access


def cpusers(_userplans_path=CPANEL_USERPLANS_PATH):
    stream = open(_userplans_path)
    users_list = [line.split(':')[0].strip() for line in stream
                  if not line.startswith('#') and line.count(':') == 1 and len(line.strip()) > 3]
    stream.close()
    return tuple(users_list)


def resellers(_resellers_path=CPANEL_RESELLERS_PATH):
    if not os.path.isfile(_resellers_path):  # on a clean system, this file may not be
        return tuple()
    stream = open(_resellers_path)
    resellers_list = [line.split(':')[0].strip() for line in stream
                      if not line.startswith('#') and (':' in line) and len(line.strip()) > 3]

    return tuple(resellers_list)


def dblogin_cplogin_pairs(cplogin_lst=None, with_system_users=False):
    """
    Get mapping between system and DB users
    @param cplogin_lst :list: list with usernames for generate mapping
    @param with_system_users :bool: add system users to result list or no.
                                    default: False
    """
    # initialize results list
    results = []
    # accept only list and tuple parameters
    uid_list = []
    for username in (cplogin_lst or []):
        try:
            uid_list.append(str(pwd.getpwnam(username).pw_uid))
        except KeyError:
            # no user exists - skip it
            uid_list.append("-1")

    # generate system command
    params = [DBMAPPING_SCRIPT]
    if not with_system_users:
        params.append("--nosys")
    params += uid_list

    output = subprocess.Popen(params, stdout=subprocess.PIPE,
                              stderr=subprocess.PIPE).stdout.read()

    # output format: "DBuser user UID"
    for line in output.split("\n"):
        line = line.strip()
        if not line:
            continue
        results.append(line.split(" ")[:2])

    return tuple(results)


def cpinfo(cpuser=None, keyls=('cplogin', 'package', 'mail', 'reseller', 'dns', 'locale'),
           _cpanel_users_dir=CPANEL_USERS_DIR):
    returned = list()
    if isinstance(cpuser, (str, unicode)):
        cpusers_list = [cpuser]
    elif isinstance(cpuser, (list, tuple)):
        cpusers_list = tuple(cpuser)
    elif cpuser is None:
        cpusers_list = glob.glob(os.path.join(_cpanel_users_dir, '*'))
    else:
        raise CpApiTypeError(funcname='cpinfo', supportettypes='str|unicode|list|tuple',
                             received_type=type(cpuser).__name__)

    for cpuser in cpusers_list:
        user_config_path = os.path.join(_cpanel_users_dir, cpuser)
        if not os.path.exists(user_config_path):
            print('WARNING: Can not load data to the user "%s"; Perhaps there is no such user in cPanel' % (cpuser,))
            continue
        cpuser_data = loadconfig(user_config_path)
        user_data = [cpuser_data.get(USERCONF_PARAM_MAP.get(data_key)) for data_key in keyls]
        returned.append(tuple(user_data))

        if 'mail' in keyls:  # checking the presence of an additional e-mail
            additional_mail = cpuser_data.get('contactemail2')
            if additional_mail:
                user_data[list(keyls).index('mail')] = additional_mail
                user_data_tuple = tuple(user_data)
                if user_data_tuple not in returned:
                    returned.append(tuple(user_data))
    return tuple(returned)


def get_admin_email(_conf1=None, _conf2=None, _hostname=None):
    """
    :param str|None _conf1: for testing
    :param str|None _conf2: for testing
    :param str|None _hostname: for testing
    :return:
    """
    # 1. Try to get admin email from /etc/sysconfig/cloudlinux
    lines = []
    try:
        f = open(_conf1 or SYSCONF_CLOUDLINUX_PATH, 'r')
        lines = f.readlines()
        f.close()
    except (OSError, IOError):
        pass

    for line in lines:
        if line.startswith('EMAIL'):
            parts = line.split('=')
            if len(parts) == 2 and '@' in parts[1].strip():
                return parts[1].strip()

    # 2. Try to get admin email from /etc/wwwacct.conf
    lines = []
    try:
        f = open(_conf2 or CPANEL_ACCT_CONF_PATH, 'r')
        lines = f.readlines()
        f.close()
    except (OSError, IOError):
        pass

    host = ''
    for line in lines:
        if line.startswith('CONTACTEMAIL'):
            s = line.replace('CONTACTEMAIL', '').strip()
            if s:
                return s
        if line.startswith('HOST'):
            s = line.replace('HOST', '').strip()
            if s:
                host = s
    if host:
        return 'root@' + host
    # Admin email not found in system files, use common address
    from clcommon.cpapi.plugins.universal import get_admin_email
    return get_admin_email(_hostname=_hostname)


def docroot(domain, _path=CPANEL_USERDATADOMAINS_PATH):
    domain = domain.strip()
    except_list = list()
    if '{user}' in _path:
        call_as_user = pwd.getpwuid(os.getuid()).pw_name
        _path = _path.replace('{user}', call_as_user)
    path_list = _path.split(';')
    for path_ in path_list:
        if not os.path.exists(path_):
            continue
        try:
            file_ = open(path_)
        except IOError, e:
            except_list.append(str(e))
            continue
        # example line:
        #test.russianguns.ru: russianguns==root==sub==russianguns.ru==/home/russianguns/fla==192.168.122.40:80======0
        for line in file_:
            if line.startswith(domain + ':'):
                domain_raw_data = line[len(domain) + 1:].strip()
                domain_data = domain_raw_data.split('==')
                docroot_path = domain_data[4]
                user = domain_data[0]
                file_.close()
                return docroot_path, user
        except_list.append('Can\'t find record "%s" in file "%s"' % (domain, path_))
        file_.close()
    raise NoDomain("Can't obtain document root for domain '%s'; %s"
                   % (domain, '; '.join(except_list)))


def userdomains(cpuser, _path=CPANEL_USERDATADOMAINS_PATH):
    domains_order = []
    result_list = []
    domains_dict = {}

    def ordered_insert(key, value, position=None):
        if key in domains_dict:
            return
        elif position is not None:
            domains_order.insert(position, domain)
        else:
            domains_order.append(domain)
        domains_dict.update({key: value})

    if '{user}' in _path:
        call_as_user = pwd.getpwuid(os.getuid()).pw_name
        _path = _path.replace('{user}', call_as_user)
    path_list = _path.split(';')
    for path_ in path_list:
        try:
            file_ = open(path_)
        except IOError:
            continue
        for line_numb, line in enumerate(file_):
            # example line:
            #test.russianguns.ru: russianguns==root==sub==russianguns.ru==/home/russianguns/fla==192.168.122.40:80======0
            if not line.strip():  # ignore the empty string
                continue
            if line.count(': ') != 1:
                print 'Can\'t pars %s line in file "%s"; line was ignored' % (line_numb, path_)
                continue
            domain, domain_raw_data = line.split(': ')
            domain_data = domain_raw_data.strip().split('==')
            user_ = domain_data[0]
            if cpuser == user_:
                document_root = domain_data[4]
                main_domain = 'main' == domain_data[2]
                if main_domain:
                    ordered_insert(domain, document_root, 0)   # main domain must be first in list
                else:
                    ordered_insert(domain, document_root)
        file_.close()
    for key in domains_order:
        result_list.append((key, domains_dict[key]))
    return result_list

def homedirs(_sysusers=None, _conf_path = CPANEL_ACCT_CONF_PATH):
    """
    Detects and returns list of folders contained the home dirs of users of the cPanel

    :param str|None _sysusers: for testing
    :param str|None _conf_path: for testing
    :return: list of folders, which are parent of home dirs of users of the panel
    """

    HOMEDIR = 'HOMEDIR '
    HOMEMATCH = 'HOMEMATCH '

    homedirs = []
    users_homedir = ''
    users_home_match = ''

    if (os.path.exists(_conf_path)):
        lines = open(_conf_path, 'r')
        for line in lines:
            if line.startswith(HOMEDIR):
                users_homedir = line.split(HOMEDIR)[1].strip()
            elif line.startswith(HOMEMATCH):
                users_home_match = line.split(HOMEMATCH)[1].strip()

    if users_homedir:
        homedirs.append(users_homedir)

    clpwd = ClPwd()
    users_dict = clpwd.get_user_dict()

    # for testing only
    if isinstance(_sysusers, (list, tuple)):
        class pw(object):
            def __init__(self, name, dir):
                self.pw_name = name
                self.pw_dir = dir

        users_dict = {}
        for (name,dir) in _sysusers:
            users_dict[name] = pw(name, dir)

    for user_name in users_dict:
        userdir = users_dict[user_name].pw_dir
        if os.path.exists(userdir + '/public_html') or os.path.exists(userdir + '/www'):
            homedir = os.path.dirname(userdir)
            if users_home_match and homedir.find('/'+users_home_match) == -1:
                continue
            if homedir not in homedirs:
                homedirs.append(homedir)

    return homedirs

#TODO: You can extract the data from the database but it needs that will automatically install the package MySQL-python
#      cPanel repository in MySQL-python disabled
#def dblogin_cplogin_pairs(cplogin_lst=None):
#    access = db_access()
#    data = _dblogin_cplogin_pairs(cplogin_lst=cplogin_lst, access=access)
#    return data
