import os
import sys
import time
import yaml
import subprocess


def run(cmd, input=None):
    """
    Run script, storing various aspects of the results.
    """
    data = {}
    data['start_time'] = time.time()
    proc = subprocess.Popen(cmd,
                            stdin=subprocess.PIPE,
                            stdout=subprocess.PIPE,
                            stderr=subprocess.STDOUT,
                            close_fds=True,
                            )
    #print("Run command {!r} with input {}".format(cmd, input))
    (stdout, stderr) = proc.communicate(input=input)
    data['end_time'] = time.time()
    data['exit_status'] = proc.returncode
    data['pid'] = proc.pid
    data['output'] = stdout
    data['stderr'] = stderr
    data['success'] = True
    if stderr is not None or proc.returncode:
        sys.stderr.write('Command {!r} FAILED (exit {}), stdout {} stderr: {}\n'.format(
            ' '.join(cmd), proc.returncode, stdout, stderr))
        data['success'] = False
    return data

def extract_comments(data):
    comments = {}
    curr = []
    for this in data.split('\n'):
        if this.startswith('#'):
            #print("COMMENT: {}".format(this))
            curr += [this]
        elif ':' in this and len(curr):
            key = this.split(':')[0]
            comments[key] = curr
            #print("KEY {} FROM {}: {}".format(key, this, curr))
            curr = []
        else:
            #print("RESET AT {}".format(this))
            curr = []
    return comments


def get_host_pubkey_fn(args, fqdn):
    fns = [os.path.join(args.certdir, '{}_infra.pem'.format(fqdn)),
           os.path.join(args.certdir, '{}.pem'.format(fqdn)),
           os.path.join(args.certdir, 'public_certkey.pkcs7.pem'),
           ]
    for fn in fns:
        if os.path.isfile(fn):
            return fn
    print('Public key file not found (tried {})'.format(pubkey_fn, fns))
    return None


def get_host_eyaml_fn(fqdn):
    return '{}/overlay/etc/hiera/data/local.eyaml'.format(fqdn)


def get_host_secrets_fn(fqdn):
    return '{}/overlay/etc/hiera/data/secrets.yaml.asc'.format(fqdn)


def get_host_secrets(fqdn):
    """
    SSH to a host and read all it's secrets.

    :param fqdn: Fully qualified hostname
    :return: Two dicts (data, comments)
    """
    data = run(['ssh',
                fqdn,
                'GNUPGHOME=/etc/hiera/gpg/ gpg -d /etc/hiera/data/secrets.yaml.asc 2>/dev/null'])

    if not data['success']:
        return False, False

    return _parse_eyaml(data['output'].decode('utf-8'))


def load_host_eyaml(fn):
    """
    Load an eyaml file and return parsed data plus comments

    :param fn: EYAML filename
    :return: Two dicts (data, comments)
    """
    if not os.path.isfile(fn):
        return {}, {}

    with open(fn) as fd:
        data_str = fd.read(1024 * 1024)
    return _parse_eyaml(data_str)


def _parse_eyaml(data):
    """
    Parse YAML data but also retain comments associated with the keys.

    :param data:
    :return: Two dicts (data, comments)
    """
    if not data:
        return False, False
    try:
        comments = extract_comments(data)
        return yaml.safe_load(data), comments
    except Exception as exc:
        sys.stderr.write('Failed to parse EYAML:\n{}\n{}\n'.format(data, exc))

    return False, False


def make_eyaml(data, comments, pubkey_fn):
    res = []
    for k, v in sorted(data.items()):
        cmd = ['eyaml',
               'encrypt',
               '--label', k,
               '--output', 'block',
               '--stdin',
               '--pkcs7-public-key', pubkey_fn,
        ]
        eyaml = run(cmd, input=bytes(str(v), 'utf-8'))
        if not eyaml['success']:
            return False
        if k in comments:
            res += comments[k]
        enc = eyaml['output'].decode('utf-8')
        if 'PKCS' not in enc:
            print("Command '{}' failed: {}".format(' '.join(cmd), enc))
        res += enc.split('\n')
    return res