#!/usr/bin/env python

from __future__ import print_function, unicode_literals # We require Python 2.6 or later
import sys
import argparse
import io
import os
import random
import re
import string
import subprocess

if sys.version_info[:3][0] == 2:
    import ConfigParser as configparser
    import StringIO as io

if sys.version_info[:3][0] == 3:
    import configparser as configparser
    import io as io


# prepare base dir
base_dir = os.path.dirname(os.path.abspath(__file__))

parser = argparse.ArgumentParser(description='Generate *.cm.yaml')
parser.add_argument('-f', default=os.path.join(base_dir, '../harbor.cfg'),
                    dest='config_file', help='[Optional] path of harbor config file')
parser.add_argument('-k', default='',
                    dest='private_key', help='[Optional] path of harbor https private key(pem)')
parser.add_argument('-c', default='',
                    dest='cert', help='[Optional] harbor path of https cert(pem)')
parser.add_argument('-j', default='',
                    dest='jobservice_secret', help="[Optional] path of harbor secret key(16 characters)")
parser.add_argument('-s', default='',
                    dest='secret_key', help="[Optional] path of harbor secret key(16 characters)")

args = parser.parse_args()

# read config file
config_str = ''
if os.path.isfile(args.config_file):
    with open(args.config_file) as conf:
        config_str = conf.read()
else:
    raise Exception('Error: No such file(' + args.config_file + ')')

config_str = '[harbor]\n' + config_str
fp = io.StringIO()
fp.write(config_str)
fp.seek(0, os.SEEK_SET)
config = configparser.RawConfigParser()
config.readfp(fp)


def get_config(key):
    """get value by key
    """
    if config.has_option('harbor', key):
        return config.get('harbor', key)
    print('Warning: Key(' + key + ') is not existing. Use empty string as default')
    return ''


def set_config(key, value):
    """set key & value 
    """
    config.set('harbor', key, value)

# relative path with config file
def rel_path(p):
    if p[0] == '/':
        return p
    config_path = args.config_file
    if config_path[0] != '/':
        config_path = os.path.join(os.getcwd(), config_path) 
    return os.path.join(os.path.dirname(config_path), p)
        
# path of private key
pk_path = args.private_key
if pk_path == '':
    pk_path = get_config('ssl_cert_key')
    if pk_path != '':
        pk_path = rel_path(pk_path)

# path of cert
cert_path = args.cert
if cert_path == '':
    cert_path = get_config('ssl_cert')
    if cert_path != '':
        cert_path = rel_path(cert_path)


# validate 
if get_config('ui_url_protocol') == 'https':
    if pk_path == '':
        raise Exception("Error: The protocol is https but attribute ssl_cert_key is not set")
    if cert_path == '':
        raise Exception("Error: The protocol is https but attribute ssl_cert is not set")
else:
    pk_path = ''
    cert_path = ''


# read jobservice secret key
if args.jobservice_secret != '':
    if os.path.isfile(args.jobservice_secret):
        key = ''
        with open(args.jobservice_secret, 'r') as skey:
            key = skey.read()
        if len(key) != 16:
            raise Exception('Error: The length of secret key has to be 16 characters!')
        set_config('jobservice_secret', key)
else:
    set_config('jobservice_secret', ''.join(random.choice(
    string.ascii_letters + string.digits) for i in range(16)))

# read ldap secret key
if args.secret_key != '':
    if os.path.isfile(args.secret_key):
        key = ''
        with open(args.secret_key, 'r') as skey:
            key = skey.read()
        if len(key) != 16:
            raise Exception('Error: The length of secret key has to be 16 characters!')
        set_config('secret_key', key)
else:
    set_config('secret_key', ''.join(random.choice(
    string.ascii_letters + string.digits) for i in range(16)))

# read https pkey & cert
if pk_path != '':
    if os.path.isfile(pk_path):
        with open(pk_path, 'r') as pkey:
            set_config('https_pkey', pkey.read())
    else:
        raise Exception('Error: https private key is not existing')
else:
    set_config('https_pkey', 'USE_HTTP')

if cert_path != '':
    if os.path.isfile(cert_path):
        with open(cert_path, 'r') as cert:
            set_config('https_cert', cert.read())
    else:
        raise Exception('Error: https cert is not existing')
else:
    set_config('https_cert', 'USE_HTTP')
    

# add configs
set_config('ui_url', get_config('ui_url_protocol') +
           '://' + get_config('hostname'))
set_config('ui_secret', ''.join(random.choice(
    string.ascii_letters + string.digits) for i in range(16)))

# generate auth pkey & cert 
with open(os.devnull, 'w') as devnull:
    openssl = subprocess.call(['which','openssl'], stdout=devnull, stderr=devnull)
    if openssl == 0:
        pkey = subprocess.check_output(['openssl','genrsa','4096'], stderr=devnull)
        empty_subj = "/C=/ST=/L=/O=/CN=/"
        openssl = subprocess.Popen(['openssl', 'req', '-new', '-x509',  '-key', '/dev/stdin', '-days', '3650', '-subj', empty_subj],
                                stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=devnull)
        cert = openssl.communicate(input=pkey)[0]
        set_config('auth_pkey', pkey.decode())
        set_config('auth_cert', cert.decode())
    else:
        set_config('auth_pkey', 'NEED_SET')
        set_config('auth_cert', 'NEED_SET')
        print('Warning: auth_pkey and auth_cert cannot be generated automatically without openssl.  Please set it manually')
    


variable = re.compile(r'{{.+?}}')
detail = re.compile(r'((\d+) )?([a-zA-Z_0-9-]+)')
def render_template(tmpl):
    """render template
    replace {{(number of leading spaces)name}} with config
    examples:
    config: 
    hostname='test\ntest'
    
    {{hostname}} -> 'test\ntest'
    {{4 hostname}} -> 'test\n    test'
    """
    matches = variable.findall(tmpl)
    for match in matches:
        segs = detail.search(match)
        if segs.group() == '':
            raise Exception('Error: Invalid template item(' + match + ')')
        value = get_config(segs.group(3))
        spaces = segs.group(2)
        if spaces != '' and spaces != None:
            leading = ''.join(' ' for i in range(int(spaces)))
            value = str(value).replace('\n', '\n' + leading)
        tmpl = tmpl.replace(match, value)
    return tmpl


def generate_template(tmpl, dest):
    """generate file
    """
    with open(tmpl) as tmpl:
        with open(dest, 'w') as dest:
            dest.write(render_template(tmpl.read()))


template_dir = os.path.join(base_dir, 'templates')
output_dir = base_dir
generate_template(os.path.join(template_dir, 'ui.cm.yaml'), os.path.join(output_dir, 'ui/ui.cm.yaml'))
generate_template(os.path.join(template_dir, 'jobservice.cm.yaml'), os.path.join(output_dir, 'jobservice/jobservice.cm.yaml'))
generate_template(os.path.join(template_dir, 'mysql.cm.yaml'), os.path.join(output_dir, 'mysql/mysql.cm.yaml'))
generate_template(os.path.join(template_dir, 'registry.cm.yaml'), os.path.join(output_dir, 'registry/registry.cm.yaml'))
generate_template(os.path.join(template_dir, 'adminserver.cm.yaml'), os.path.join(output_dir, 'adminserver/adminserver.cm.yaml'))
generate_template(os.path.join(template_dir, 'ingress.yaml'), os.path.join(output_dir, 'ingress.yaml'))