import csv
import os
import re

from wo.core.fileutils import WOFileUtils
from wo.core.logging import Log
from wo.core.shellexec import WOShellExec
from wo.core.variables import WOVar
from wo.core.acme import WOAcme


class SSL:

    def getexpirationdays(self, domain, returnonerror=False):
        # check if exist
        if not os.path.exists('/etc/letsencrypt/live/{0}/cert.pem'
                              .format(domain)):
            Log.debug(self, "cert not found for {0}".format(domain))

            split_domain = domain.split('.')
            root_domain = ('.').join(split_domain[1:])

            Log.debug(self, "trying with {0}".format(root_domain))
            if os.path.exists('/etc/letsencrypt/live/{0}/cert.pem'
                              .format(root_domain)):
                domain = root_domain
            else:
                Log.error(self, 'File Not Found: '
                          '/etc/letsencrypt/live/{0}/cert.pem'
                          .format(domain), False)
                Log.error(
                    self, "Check the WordOps log for more details "
                          "`tail /var/log/wo/wordops.log` "
                          "and please try again...")
        Log.debug(
            self,
            "Getting expiration of /etc/letsencrypt/live/{0}/cert.pem"
            .format(domain))
        current_date = WOShellExec.cmd_exec_stdout(self, "date -d \"now\" +%s")
        expiration_date = WOShellExec.cmd_exec_stdout(
            self, "date -d \"$(openssl x509 -in /etc/letsencrypt/live/"
            "{0}/cert.pem -text -noout | grep \"Not After\" "
            "| cut -c 25-)\" +%s"
            .format(domain))

        days_left = int((int(expiration_date) - int(current_date)) / 86400)
        if (days_left > 0):
            return days_left
        else:
            # return "Certificate Already Expired ! Please Renew soon."
            return -1

    def getexpirationdate(self, domain):
        # check if exist
        if not os.path.isfile('/etc/letsencrypt/live/{0}/cert.pem'
                              .format(domain)):
            if os.path.exists('/var/www/{0}/conf/nginx/ssl.conf'):
                split_domain = domain.split('.')
                check_domain = ('.').join(split_domain[1:])
            else:
                Log.error(
                    self, 'File Not Found: /etc/letsencrypt/'
                    'live/{0}/cert.pem'
                    .format(domain), False)
                Log.error(
                    self, "Check the WordOps log for more details "
                    "`tail /var/log/wo/wordops.log` and please try again...")
        else:
            check_domain = domain

        return WOShellExec.cmd_exec_stdout(
            self, "date -d \"$(/usr/bin/openssl x509 -in "
            "/etc/letsencrypt/live/{0}/cert.pem -text -noout | grep "
            "\"Not After\" | cut -c 25-)\" "
            .format(check_domain))

    def siteurlhttps(self, domain):
        wo_site_webroot = ('/var/www/{0}'.format(domain))
        WOFileUtils.chdir(
            self, '{0}/htdocs/'.format(wo_site_webroot))
        if WOShellExec.cmd_exec(
                self, "{0} --allow-root core is-installed"
                .format(WOVar.wo_wpcli_path)):
            wo_siteurl = (
                WOShellExec.cmd_exec_stdout(
                    self, "{0} option get siteurl "
                    .format(WOVar.wo_wpcli_path) +
                    "--allow-root --quiet"))
            test_url = re.split(":", wo_siteurl)
            if not (test_url[0] == 'https'):
                Log.wait(self, "Updating site url with https")
                try:
                    WOShellExec.cmd_exec(
                        self, "{0} option update siteurl "
                        "\'https://{1}\' --allow-root"
                        .format(WOVar.wo_wpcli_path, domain))
                    WOShellExec.cmd_exec(
                        self, "{0} option update home "
                        "\'https://{1}\' --allow-root"
                        .format(WOVar.wo_wpcli_path, domain))
                    WOShellExec.cmd_exec(
                        self, "{0} search-replace \'http://{1}\'"
                        "\'https://{1}\' --skip-columns=guid "
                        "--skip-tables=wp_users --allow-root"
                        .format(WOVar.wo_wpcli_path, domain))
                except Exception as e:
                    Log.debug(self, str(e))
                    Log.failed(self, "Updating site url with https")
                else:
                    Log.valide(self, "Updating site url with https")

    # check if a wildcard exist to secure a new subdomain
    def checkwildcardexist(self, wo_domain_name):
        """Check if a wildcard certificate exist for a domain"""

        wo_acme_exec = ("/etc/letsencrypt/acme.sh --config-home "
                        "'/etc/letsencrypt/config'")
        # export certificates list from acme.sh
        WOShellExec.cmd_exec(
            self, "{0} ".format(wo_acme_exec) +
            "--list --listraw > /var/lib/wo/cert.csv")

        # define new csv dialect
        csv.register_dialect('acmeconf', delimiter='|')
        # open file
        certfile = open('/var/lib/wo/cert.csv', mode='r', encoding='utf-8')
        reader = csv.reader(certfile, 'acmeconf')
        wo_wildcard_domain = ("*.{0}".format(wo_domain_name))
        for row in reader:
            if wo_wildcard_domain == row[2]:
                if not row[3] == "":
                    return True
        certfile.close()
        return False

    def setuphsts(self, wo_domain_name, enable=True):
        """Enable or disable htsts for a site"""
        if enable:
            if WOFileUtils.enabledisable(
                self, '/var/www/{0}/conf/nginx/hsts.conf'
            ):
                return 0
            else:
                Log.info(
                    self, "Adding /var/www/{0}/conf/nginx/hsts.conf"
                    .format(wo_domain_name))

                hstsconf = open("/var/www/{0}/conf/nginx/hsts.conf"
                                .format(wo_domain_name),
                                encoding='utf-8', mode='w')
                hstsconf.write("more_set_headers "
                               "\"Strict-Transport-Security: "
                               "max-age=31536000; "
                               "includeSubDomains; "
                               "preload\";")
                hstsconf.close()
                return 0
        else:
            if WOFileUtils.enabledisable(
                self, '/var/www/{0}/conf/nginx/hsts.conf',
                enable=False
            ):
                Log.info(self, "HSTS disabled")
                return 0
            else:
                Log.info(self, "HSTS is not enabled")
                return 0

    def selfsignedcert(self, proftpd=False, backend=False):
        """issue a self-signed certificate"""

        selfs_tmp = '/var/lib/wo/tmp/selfssl'
        # create self-signed tmp directory
        if not os.path.isdir(selfs_tmp):
            WOFileUtils.mkdir(self, selfs_tmp)
        try:
            WOShellExec.cmd_exec(
                self, "openssl genrsa -out "
                "{0}/ssl.key 2048"
                .format(selfs_tmp))
            WOShellExec.cmd_exec(
                self, "openssl req -new -batch  "
                "-subj /commonName=localhost/ "
                "-key {0}/ssl.key -out {0}/ssl.csr"
                .format(selfs_tmp))

            WOFileUtils.mvfile(
                self, "{0}/ssl.key"
                .format(selfs_tmp),
                "{0}/ssl.key.org"
                .format(selfs_tmp))

            WOShellExec.cmd_exec(
                self, "openssl rsa -in "
                "{0}/ssl.key.org -out "
                "{0}/ssl.key"
                .format(selfs_tmp))

            WOShellExec.cmd_exec(
                self, "openssl x509 -req -days "
                "3652 -in {0}/ssl.csr -signkey {0}"
                "/ssl.key -out {0}/ssl.crt"
                .format(selfs_tmp))

        except Exception as e:
            Log.debug(self, "{0}".format(e))
            Log.error(
                self, "Failed to generate HTTPS "
                "certificate for 22222", False)
        if backend:
            WOFileUtils.mvfile(
                self, "{0}/ssl.key"
                .format(selfs_tmp),
                "/var/www/22222/cert/22222.key")
            WOFileUtils.mvfile(
                self, "{0}/ssl.crt"
                .format(selfs_tmp),
                "/var/www/22222/cert/22222.crt")
        if proftpd:
            WOFileUtils.mvfile(
                self, "{0}/ssl.key"
                .format(selfs_tmp),
                "/etc/proftpd/ssl/proftpd.key")
            WOFileUtils.mvfile(
                self, "{0}/ssl.crt"
                .format(selfs_tmp),
                "/etc/proftpd/ssl/proftpd.crt")
        # remove self-signed tmp directory
        WOFileUtils.rm(self, selfs_tmp)

    def httpsredirect(self, wo_domain_name, acme_domains, redirect=True):
        """Create Nginx redirection from http to https"""
        wo_acme_domains = ' '.join(acme_domains)
        if redirect:
            Log.wait(self, "Adding HTTPS redirection")
            if WOFileUtils.enabledisable(
                    self, '/etc/nginx/conf.d/force-ssl-{0}.conf'
                    .format(wo_domain_name), enable=True):
                Log.valide(self, "Adding HTTPS redirection")
                return 0
            else:
                try:
                    sslconf = open(
                        "/etc/nginx/conf.d/force-ssl-{0}.conf"
                        .format(wo_domain_name),
                        encoding='utf-8', mode='w')
                    sslconf.write(
                        "server {\n"
                        "\tlisten 80;\n" +
                        "\tlisten [::]:80;\n" +
                        "\tserver_name {0};\n"
                        .format(wo_acme_domains) +
                        "\treturn 301 https://$host"
                        "$request_uri;\n}")
                    sslconf.close()
                except IOError as e:
                    Log.debug(self, str(e))
                    Log.debug(
                        self, "Error occured while generating "
                        "/etc/nginx/conf.d/force-ssl-{0}.conf"
                        .format(wo_domain_name))
                    return 1
                Log.valide(self, "Adding HTTPS redirection")
                return 0
        else:
            if WOFileUtils.enabledisable(
                    self, "/etc/nginx/conf.d/force-ssl-{0}.conf"
                    .format(wo_domain_name), enable=False):
                Log.info(
                    self, "Disabled HTTPS Force Redirection for site "
                    "{0}".format(wo_domain_name))
            else:
                Log.info(
                    self, "HTTPS redirection already disabled for site"
                    "{0}".format(wo_domain_name)
                )
            return 0

    def archivedcertificatehandle(self, domain, acme_domains):
        Log.warn(
            self, "You already have an existing certificate "
            "for the domain requested.\n"
            "(ref: {0}/"
            "{1}_ecc/{1}.conf)".format(WOVar.wo_ssl_archive, domain) +
            "\nPlease select an option from below?"
            "\n\t1: Reinstall existing certificate"
            "\n\t2: Issue a new certificate to replace "
            "the current one (limit ~5 per 7 days)"
            "")
        check_prompt = input(
            "\nType the appropriate number [1-2] or any other key to cancel: ")
        if not os.path.isfile("{0}/{1}/fullchain.pem"
                              .format(WOVar.wo_ssl_live, domain)):
            Log.debug(
                self, "{0}/{1}/fullchain.pem file is missing."
                .format(WOVar.wo_ssl_live, domain))
            check_prompt = "2"

        if check_prompt == "1":
            Log.info(self, "Reinstalling SSL cert with acme.sh")
            ssl = WOAcme.deploycert(self, domain)
            if ssl:
                SSL.httpsredirect(self, domain, acme_domains)

        elif (check_prompt == "2"):
            Log.info(self, "Issuing new SSL cert with acme.sh")
            ssl = WOShellExec.cmd_exec(
                self, "/etc/letsencrypt/acme.sh "
                "--config-home '/etc/letsencrypt/config' "
                "--renew -d {0} --ecc --force"
                .format(domain))

            if ssl:
                WOAcme.deploycert(self, domain)
        else:
            Log.error(self, "Operation cancelled by user.")

        if os.path.isfile("{0}/conf/nginx/ssl.conf"
                          .format(domain)):
            Log.info(self, "Existing ssl.conf . Backing it up ..")
            WOFileUtils.mvfile(self, "/var/www/{0}/conf/nginx/ssl.conf"
                               .format(domain),
                               '/var/www/{0}/conf/nginx/ssl.conf.bak'
                               .format(domain))

        return ssl
