#!/usr/bin/python3.6
# coding: utf8


import locale
from dialog import Dialog
import os
import pynginxconfig
import re
from goto import with_goto
# import sys
# import getopt
import subprocess
import shlex


nginx_dir = "/etc/nginx"
nginx_available_sites = nginx_dir + "/sites_available/"
nginx_enabled_sites = nginx_dir + "/sites_enabled/"
nginx_templates = nginx_dir + "/templates/"
template_host = 'host.conf'
template_ssl = 'host_ssl.conf'


errcode = list()
errcode.append(False)
errcode.append(True)
errcode.append("string can't be empty")
errcode.append('English letters only. To issue for an Internationalized Domain Name, use Punycode.')
errcode.append('string must be numerical')
errcode.append('')


def get_available():
    f = os.listdir(nginx_available_sites)
    return f


def check_if_enabled(vhost_file):
    if os.path.exists(nginx_enabled_sites + vhost_file):
        return True
    else:
        return False


def check_cert(domain):
    if os.path.isfile('/etc/letsencrypt/live/' + domain.split(' ')[0] + '/cert.pem'):
        return True
    else:
        return False


def make_list(delete=False):
    t = tuple()
    x = get_available()
    a = list()
    for vhost in x:
        if delete is not True:
            t = (vhost, get_server_name(vhost), check_if_enabled(vhost))
            a.append(t)
        else:
            t = (vhost, get_server_name(vhost), False)
            a.append(t)
    s = sorted(a, key=lambda dom: dom[1])
    return s


def enable_vhost(vhost):
    try:
        os.symlink(nginx_available_sites + vhost, nginx_enabled_sites + vhost)
    except FileExistsError:
        pass
    return True


def renew_enabled(vhosts):
    a = get_available()
    for vhost in a:
        if vhost in vhosts:
            if check_if_enabled(vhost):
                pass
            else:
                enable_vhost(vhost)
        else:
            if check_if_enabled(vhost):
                os.unlink(nginx_enabled_sites + vhost)
            else:
                pass
    restart_nginx()
    return True


def get_server_name(vhost_file):
    i = int()
    if vhost_file == 'default.conf':
        ret = 'default.conf'
    else:
        vhost = pynginxconfig.NginxConfig()
        vhost.loadf(nginx_available_sites + vhost_file)
        try:
            ret = vhost.get([('server',), 'server_name'])[1]
        except:
            ret = "None"
    return ret


def enable_redirect(cfg):
    filename = cfg
    vhost = pynginxconfig.NginxConfig()
    vhost.loadf(nginx_available_sites + filename)
    vhost[0]['value'].insert(7, ('rewrite', '^ https://$http_host$request_uri? permanent'))
    vhost.savef(nginx_available_sites + filename)
    return


def is_ascii(s):
    pattern = r'[^.a-zA-Z0-9- ]'
    if len(s) != 0:
        if re.search(pattern, s):
            return 3
        else:
            return 1
    else:
        return 2


def is_num(s):
    pattern = r'[^0-9]'
    if len(s) != 0:
        if re.search(pattern, s):
            return 4
        else:
            return 1
    else:
        return 2


def check_data(data):
    msg = list()
    ret = True
    rc = is_ascii(data['Domain'])
    if rc != True:
        ret = False
        msg.append("Domain: " + str(errcode[rc]))

    rc = is_ascii(data['SubDomain'])
    if data['SubDomain'] != '':
        if rc != True:
            ret = False
            msg.append("SubDomain: " + str(errcode[rc]))

    rc = is_ascii(data['Backend_Addr'])
    if rc != True:
        ret = False
        msg.append("Backend_Addr: " + str(errcode[rc]))

    rc = is_num(data['Backend_Port'])
    if rc != True:
        ret = False
        msg.append("Backend_Port: " + str(errcode[rc]))

    return ret, msg


def delete_vhosts(vhosts):
    for vhost in vhosts:
        try:
            os.unlink(nginx_enabled_sites + vhost)
        except:
            pass
        try:
            os.unlink(nginx_available_sites + vhost)
        except:
            pass
    restart_nginx()


def form_one():
    locale.setlocale(locale.LC_ALL, '')
    d = Dialog(dialog="dialog")
    d.set_background_title("NGINX domains managment")

    ret, tags = d.menu("Select action?", width=60, height=10,
                    choices=[("Add",    "Add new domain"),
                             ("Enable/Disable", "Enable/Disable selected domains"),
                             ("Delete", "Delete selected domains")],
                    title="")
    return ret, tags


def edit_form(domain='', subdomain='', backend_addr='', backend_port='80'):
    locale.setlocale(locale.LC_ALL, '')
    d = Dialog(dialog="dialog")
    ret, val = d.mixedform("Fill the form:\n \n Enter additional domians separate by space\n (for example: www.domain.ru xxx.domain.ru)", height=14, width=60,
                        elements=[("Domain name",           1, 3, domain,       1, 22, 30, 255, 0),
                                 ("Additional domains",     2, 3, subdomain,    2, 22, 30, 65535, 0),
                                 ("Backend addr",           3, 3, backend_addr, 3, 22, 30, 255, 0),
                                 ("Backend port",           4, 3, backend_port, 4, 22, 30, 5, 0)]
                        )

    if ret == 'ok':
        ret = show_yesno('Do you want to do automatically redirect from HTTP to HTTPS?')
        if ret == 'ok':
            values = {'Domain': val[0], 'SubDomain': val[1], 'Backend_Addr': val[2], 'Backend_Port': val[3], 'redirect': True}
        else:
            values = {'Domain': val[0], 'SubDomain': val[1], 'Backend_Addr': val[2], 'Backend_Port': val[3], 'redirect': False}
        return ret, values
    else:
        return ret, ""


def select_form():
    vlist = make_list()
    locale.setlocale(locale.LC_ALL, '')
    d = Dialog(dialog="dialog")
    ret, dom_list = d.checklist("Enable or disable sites", height=30, width=120, list_height=30, choices=vlist)
    if ret == "ok":
        show_info("Please wait. Work in progress")
        renew_enabled(dom_list)
    return ret


def delete_form():
    vlist = make_list(True)
    locale.setlocale(locale.LC_ALL, '')
    d = Dialog(dialog="dialog")
    ret, dom_list = d.checklist("Select to delete:", height=30, width=120, list_height=30, choices=vlist)
    if ret == "ok":
        if show_yesno('Are you sure?\n\nThese domains will be deleted:\n\n' + '\n'.join(dom_list)) == "ok":
            show_info("Please wait. Work in progress")
            delete_vhosts(dom_list)
    else:
        return ret


def show_yesno(msg):
    locale.setlocale(locale.LC_ALL, '')
    d = Dialog(dialog="dialog")
    ret = d.yesno(msg, height=10, width=60)
    return ret


def create_domain(vhost, ssl=False):
    if ssl is not True:
        filename = vhost['Domain'] + '.conf'
        template = nginx_templates + template_host
    else:
        filename = vhost['Domain'] + '_ssl.conf'
        template = nginx_templates + template_ssl

    domains = vhost['Domain'] + ' '
    for dom in vhost['SubDomain']:
        domains += dom
    vconfig = pynginxconfig.NginxConfig()
    vconfig.loadf(template)
    server_str = vconfig.get([('server',), 'server_name'])[1].replace('[domain_names]', domains)
    log_str = vconfig.get([('server',), 'error_log'])[1].replace('[domain_name]', vhost['Domain'])
    if ssl:
        cert_str = vconfig.get([('server',), 'ssl_certificate'])[1].replace('[domain_name]', vhost['Domain'])
        cert_key_str = vconfig.get([('server',), 'ssl_certificate_key'])[1].replace('[domain_name]', vhost['Domain'])
        vconfig.set([('server',), 'ssl_certificate'], cert_str)
        vconfig.set([('server',), 'ssl_certificate_key'], cert_key_str)
    backend_str = vconfig.get([('server',), 'set'])[1].replace('[host_name]', vhost['Backend_Addr']).replace('[host_port]', vhost['Backend_Port'])
    vconfig.set([('server',), 'server_name'], server_str)
    vconfig.set([('server',), 'error_log'], log_str)
    vconfig.set([('server',), 'set'], backend_str)
    #if vhost['redirect'] is not True:
    #    vconfig.remove([('server',), 'rewrite'])
    vconfig.savef(nginx_available_sites + filename)
    return domains, filename


def create_cert(domains):
    if check_cert(domains) is not True:
        cmd = 'certbot certonly --agree-tos --email admin@shopband.ru --webroot -w /var/www/lets -d ' + domains.rstrip().replace(' ', ' -d ')
        ret = subprocess.run(shlex.split(cmd), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        if ret.returncode != 0:
            msg = "Error getting ssl certs\nThe SSL configuration is written to " + nginx_available_sites + ", " \
                "but not enabled.\nGet SSL certs manualy and make symlink to " + nginx_enabled_sites
            show_error(msg + "\nLog saved into " + domains.split(' ')[0] + ".log")
            f = open('./' + domains.split(' ')[0] + ".log", 'w')
            f.write(ret.stderr.decode("utf-8"))
            f.close()
            return False
        elif ret.returncode == 0:
            return True
        else:
            return False
    else:
        return True


def restart_nginx():
    ret = subprocess.run(shlex.split('/usr/sbin/nginx -t -c /etc/nginx/nginx.conf'), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    if ret.returncode != 0:
        show_error(ret.stderr.decode("utf-8"))
        return False
    elif ret.returncode == 0:
        ret = subprocess.run(shlex.split('service nginx restart'), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        if ret.returncode != 0:
            show_error(ret.stderr.decode("utf-8"))
            return False
        return True


def show_info(msg):
    s_width = len(msg)
    if s_width > 56:
        s_width = 56
    d = Dialog(dialog="dialog")
    d.infobox(msg, height=3, width=s_width+4)
    return


def show_error(msg):
    txt = ''
    if isinstance(msg, list):
        for err in msg:
            txt = txt + err + '\n'
    else:
        txt = msg
    locale.setlocale(locale.LC_ALL, '')
    d = Dialog(dialog="dialog")
    d.msgbox(text=txt, width=80, height=20)
    return txt


def enable_site(cfg, domains='', ssl=False):
    if ssl is True:
        if create_cert(domains):
            enable_vhost(cfg)
        else:
            return False
    else:
        enable_vhost(cfg)

    if restart_nginx():
        return True
    else:
        return False

#    if ssl:
#        ret_code = create_cert(domains)
#    if ret_code:
#        enable_vhost(filename)
#    else:
#        show_error(msg)
#    else:
#        enable_vhost(filename)
    pass


@with_goto
def start_it():
    label .start
    vals = {'Domain': '', 'SubDomain': '', 'Backend_Addr': '', 'Backend_Port': '80'}
    rc, action = form_one()
    if rc == 'ok':
        if action == "Add":
            label .add
            ret, vals = edit_form(vals['Domain'], vals['SubDomain'], vals['Backend_Addr'], vals['Backend_Port'])
            if ret is not 'ok':
                goto .start
            else:
                ret_code, msg = check_data(vals)
                if ret_code is not True:
                    show_error(msg)
                    goto .add
                else:
                    # Create HTTP config
                    domains, cfg = create_domain(vals)
                    show_info("Please wait. Work in progress")
                    if enable_site(cfg) is not True:
                        goto .start
                    else:
                        http_config = cfg

                    # Create HTTPS config
                    domains, cfg = create_domain(vals, True)
                    show_info("Please wait. Work in progress")
                    if enable_site(cfg, domains, True) is not True:
                        print("false")
                        goto .start
                    else:
                        if vals['redirect'] is True:
                            enable_redirect(http_config)
                            restart_nginx()
                    goto .start
        elif action == "Enable/Disable":
            label .endis
            ret = select_form()
            goto .start
        elif action == "Delete":
            label .delete
            delete_form()
            goto .start

start_it()