#!/usr/bin/python
# -*- coding: utf-8 -*-
# from ansible.module_utils.basic import *
from ansible.module_utils.basic import AnsibleModule
import re
import subprocess

DOCUMENTATION = '''
---
module: dokku_certs
short_description: Manages ssl configuration for an app.
description:
  - Manages ssl configuration for an app.
  - Will not update certificates
  - Only checks for presence of the crt file, not the key
options:
  app:
    description:
      - The name of the app
    required: True
    default: null
    aliases: []
  key:
    description:
      - Path to the ssl certificate key
    required: True
    default: null
    aliases: []
  cert:
    description:
      - Path to the ssl certificate
    required: True
    default: null
    aliases: []
  state:
    description:
      - The state of the ssl configuration
    required: False
    default: present
    choices: [ "present", "absent" ]
    aliases: []
author: Jose Diaz-Gonzalez
requirements: [ ]
'''

EXAMPLES = '''
- name: Adds an ssl certificate and key to an app
  dokku_certs:
    app: hello-world
    key: /etc/nginx/ssl/hello-world.key
    cert: /etc/nginx/ssl/hello-world.crt

- name: Removes an ssl certificate and key from an app
  dokku_certs:
    app: hello-world
    state: absent
'''


def force_list(l):
    if isinstance(l, list):
        return l
    return list(l)


def to_bool(v):
    return v.lower() == 'true'


def subprocess_check_output(command, split='\n'):
    error = None
    output = []
    try:
        output = subprocess.check_output(command, shell=True)
        if isinstance(output, bytes):
            output = output.decode("utf-8")
        output = str(output).rstrip('\n').split(split)
        output = force_list(filter(None, output))
        output = [o.strip() for o in output]
    except subprocess.CalledProcessError as e:
        error = str(e)
    return output, error


def dokku_certs_report(data):
    command = 'dokku --quiet certs:report {0}'.format(data['app'])
    output, error = subprocess_check_output(command)
    if error is not None:
        return output, error
    output = [re.sub('\s\s+', '', line) for line in output]
    report = {}

    allowed_keys = [
        'dir',
        'enabled',
        'hostnames',
        'expires at',
        'issuer',
        'starts at',
        'subject',
        'verified',
    ]
    RE_PREFIX = re.compile('^ssl-')
    for line in output:
        if ':' not in line:
            continue
        key, value = line.split(':', 1)
        key = RE_PREFIX.sub(r'', key.replace(' ', '-').lower())
        if key not in allowed_keys:
            continue

        if key == 'enabled':
            value = to_bool(value)
        report[key] = value

    return report, error


def dokku_certs_absent(data=None):
    has_changed = False
    is_error = True
    meta = {'present': True}

    report, error = dokku_certs_report(data)
    if error:
        meta['error'] = error
        return (is_error, has_changed, meta)

    if not report['enabled']:
        is_error = False
        meta['present'] = False
        return (is_error, has_changed, meta)

    command = 'dokku --quiet certs:remove {0}'.format(data['app'])
    try:
        subprocess.check_call(command, shell=True)
        is_error = False
        has_changed = True
        meta['present'] = False
    except subprocess.CalledProcessError as e:
        meta['error'] = str(e)

    return (is_error, has_changed, meta)


def dokku_certs_present(data):
    is_error = True
    has_changed = False
    meta = {'present': False}

    report, error = dokku_certs_report(data)
    if error:
        meta['error'] = error
        return (is_error, has_changed, meta)

    if report['enabled']:
        is_error = False
        meta['present'] = False
        return (is_error, has_changed, meta)

    command = 'dokku certs:add {0} {1} {2}'.format(
        data['app'], data['cert'], data['key'])
    try:
        subprocess.check_call(command, shell=True)
        is_error = False
        has_changed = True
        meta['present'] = True
    except subprocess.CalledProcessError as e:
        meta['error'] = str(e)

    return (is_error, has_changed, meta)


def main():
    fields = {
        'app': {
            'required': True,
            'type': 'str',
        },
        'key': {
            'required': False,
            'type': 'str',
        },
        'cert': {
            'required': False,
            'type': 'str',
        },
        'state': {
            'required': False,
            'default': 'present',
            'choices': [
                'present',
                'absent'
            ],
            'type': 'str',
        },
    }
    choice_map = {
        'present': dokku_certs_present,
        'absent': dokku_certs_absent,
    }

    module = AnsibleModule(
        argument_spec=fields,
        supports_check_mode=False
    )
    is_error, has_changed, result = choice_map.get(
        module.params['state'])(module.params)

    if is_error:
        module.fail_json(msg=result['error'], meta=result)
    module.exit_json(changed=has_changed, meta=result)


if __name__ == '__main__':
    main()
