# -*- coding: utf-8 -*-

import json
import os
import requests
import shutil
import subprocess
import urllib

from . import crypto
from . import templates
from . import net
from .paths import ACME_CRON, ACME_DIR, ISSUE_FILE, MOTD_FILE, NGINX_DIR, RELOAD_URL

class VMMgr:
    def __init__(self, conf):
        # Load JSON configuration
        self.conf = conf
        self.domain = conf['host']['domain']
        self.port = conf['host']['port']

    def register_app(self, app, host, login, password):
        # Register newly installed application, its subdomain and credentials (called at the end of package install.sh)
        self.conf['apps'][app] = {'host': host,
                                  'login': login if login else 'N/A',
                                  'password': password if password else 'N/A',
                                  'visible': False}
        self.conf.save()
        self.reload_wsgi_config()

    def unregister_app(self, app):
        # Unregister application during uninstallation (called at the end of package uninstall.sh)
        if app not in self.conf['apps']:
            return
        del self.conf['apps'][app]
        self.conf.save()
        self.reload_wsgi_config()

    def reload_wsgi_config(self):
        # Attempt to contact running vmmgr WSGI application to reload config
        try:
            requests.get(RELOAD_URL, timeout=3)
        except:
            pass

    def register_proxy(self, app):
        # Setup proxy configuration and reload nginx
        with open(os.path.join(NGINX_DIR, '{}.conf'.format(app)), 'w') as f:
            f.write(templates.NGINX.format(app=app, host=self.conf['apps'][app]['host'], domain=self.conf['host']['domain'], port=self.conf['host']['port']))
        self.reload_nginx()

    def unregister_proxy(self, app):
        # Remove proxy configuration and reload nginx
        os.unlink(os.path.join(NGINX_DIR, '{}.conf'.format(app)))
        self.reload_nginx()

    def update_host(self, domain, port):
        # Update domain and port, rebuild all configuration and restart nginx
        self.domain = self.conf['host']['domain'] = domain
        self.port = self.conf['host']['port'] = port
        self.conf.save()
        # Rebuild nginx config for the portal app. Web interface calls restart_nginx() in WSGI close handler
        with open(os.path.join(NGINX_DIR, 'default.conf'), 'w') as f:
            f.write(templates.NGINX_DEFAULT.format(port=self.port, domain_esc=self.domain.replace('.', '\\.')))

    def reload_nginx(self):
        subprocess.run(['/usr/sbin/nginx', '-s', 'reload'])

    def restart_nginx(self):
        subprocess.run(['/sbin/service', 'nginx', 'restart'])

    def rebuild_issue(self):
        # Compile the URLs displayed in terminal banner and rebuild the issue and motd files
        issue = templates.ISSUE.format(url=net.compile_url(self.domain, self.port), ip=net.compile_url(net.get_local_ip(), self.port))
        with open(ISSUE_FILE, 'w') as f:
            f.write(issue)
        with open(MOTD_FILE, 'w') as f:
            f.write(issue)

    def update_common_settings(self, email, gmaps_api_key):
        # Update common configuration values
        self.conf['common']['email'] = email
        self.conf['common']['gmaps-api-key'] = gmaps_api_key
        self.conf.save()

    def update_password(self, oldpassword, newpassword):
        # Update LUKS password and adminpwd for WSGI application
        pwinput = '{}\n{}'.format(oldpassword, newpassword).encode()
        partition_uuid = open('/etc/crypttab').read().split()[1][5:]
        partition_name = subprocess.run(['/sbin/blkid', '-U', partition_uuid], check=True, stdout=subprocess.PIPE).stdout.decode().strip()
        subprocess.run(['cryptsetup', 'luksChangeKey', partition_name], input=pwinput, check=True)
        # Update bcrypt-hashed password in config
        self.conf['host']['adminpwd'] = crypto.adminpwd_hash(newpassword)
        # Save config to file
        self.conf.save()

    def create_selfsigned_cert(self):
        # Disable acme.sh cronjob
        os.chmod(ACME_CRON, 0o640)
        # Create selfsigned certificate with wildcard alternative subject name
        crypto.create_selfsigned_cert(self.domain)
        # Reload nginx
        self.reload_nginx()

    def request_acme_cert(self):
        # Remove all possible conflicting certificates requested in the past
        certs = [i for i in os.listdir(ACME_DIR) if i not in ('account.conf', 'ca', 'http.header')]
        for cert in certs:
            if cert != self.domain:
                subprocess.run(['/usr/bin/acme.sh', '--remove', '-d', cert])
        # Compile an acme.sh command for certificate requisition only if the certificate hasn't been requested before
        if not os.path.exists(os.path.join(ACME_DIR, self.domain)):
            cmd = ['/usr/bin/acme.sh', '--issue', '-d', self.domain]
            for app in self.conf['apps'].copy():
                cmd += ['-d', '{}.{}'.format(self.conf['apps'][app]['host'], self.domain)]
            cmd += ['-w', ACME_DIR]
            # Request the certificate
            subprocess.run(cmd, check=True)
        # Otherwise just try to renew
        else:
            # Acme.sh returns code 2 on skipped renew
            try:
                subprocess.run(['/usr/bin/acme.sh', '--renew', '-d', self.domain], check=True)
            except subprocess.CalledProcessError as e:
                if e.returncode != 2:
                    raise
        # Install the issued certificate
        subprocess.run(['/usr/bin/acme.sh', '--install-cert', '-d', self.domain, '--key-file', crypto.CERT_KEY_FILE, '--fullchain-file', crypto.CERT_PUB_FILE, '--reloadcmd', '/sbin/service nginx reload'], check=True)
        # Enable acme.sh cronjob
        os.chmod(ACME_CRON, 0o750)

    def install_manual_cert(self, public_file, private_file):
        # Disable acme.sh cronjob
        os.chmod(ACME_CRON, 0o640)
        # Copy certificate files
        shutil.copyfile(public_file, crypto.CERT_PUB_FILE)
        shutil.copyfile(private_file, crypto.CERT_KEY_FILE)
        os.chmod(crypto.CERT_KEY_FILE, 0o600)
        # Reload nginx
        self.reload_nginx()

    def shutdown_vm(self):
        subprocess.run(['/sbin/poweroff'])

    def reboot_vm(self):
        subprocess.run(['/sbin/reboot'])
