"""
Tasks for working with the database attached to the API.

:copyright: Copyright 2013-2017, Dorian Puła
    <dorian.pula@amber-penguin-software.ca>
:license: AGPL v3+
"""

import http

import invoke as inv
import requests

from rookeries import database as rookeries_db


def _display_success(success, exit_on_failure=False):
    status = rookeries_db.OpStatus.OK if success else \
        rookeries_db.OpStatus.FAILED
    status.print()
    if not success and exit_on_failure:
        exit(1)


def _create_db(db, database_name, description):
    print(f'Creating {description} database...')

    if db.exists_db(database_name):
        rookeries_db.OpStatus.SKIP.print()
        return

    success = db.create_db(database_name)
    _display_success(success)


@inv.task()
def init_couchdb(ctx):
    """
    Initialize a configured CouchDB server.

    :param ctx: The context of the invoke task.
    """
    db = rookeries_db.CouchDB.configure_from_env()
    print(f'Initializing CouchDB for Rookeries on ${db.server_url()}...')
    _create_db(db, '_users', 'system users')
    _create_db(db, '_replicator', 'replication')
    _create_db(db, '_global_changes', 'change feed')

    print('Enforcing user authentication for requests...')
    config_url = '/_node/nonode@nohost/_config/chttpd/require_valid_user'
    response = requests.put(
        url=f'{db.server_url()}{config_url}',
        data='"true"'
    )

    _display_success(
        response.status_code == http.HTTPStatus.OK,
        exit_on_failure=True
    )


@inv.task()
def add_db(ctx, database=''):
    """
    Add a database.

    :param ctx: The context of the invoke task.
    :param database: The database to add.  Defaults to the database in the
        ROOKERIES_COUCHDB variable.
    """
    db = rookeries_db.CouchDB.configure_from_env()
    database = database if database else db.database
    _create_db(db, database, 'Rookeries DB')


@inv.task()
def add_user(ctx, username='', password=''):
    """
    Adds an admin database user.

    :param ctx: The context of the invoke task.
    :param username: The username for the new admin user.
    :param password: The password for the new admin user.
    :return:
    """

    db = rookeries_db.CouchDB.configure_from_env()
    print(f'Creating {username} user if missing...')

    user_response = requests.put(
        url=f'{db.server_url()}/_users/org.couchdb.user:{username}',
        json={
            'name': username,
            'type': 'user',
            'roles': [],
            'password': password
        }
    )

    _display_success(
        user_response.status_code == http.HTTPStatus.CREATED,
        exit_on_failure=True)

    print(f'Grant {username} user admin permissions to {db.database}')

    grant_response = requests.put(
        url=f'{db.connection()}/_security',
        json={
            'admins': {
                'names': [username],
                'roles': []
            },
            'members': {
                'names': [],
                'roles': []
            }
        })

    _display_success(
        grant_response.status_code == http.HTTPStatus.OK,
        exit_on_failure=True)


@inv.task()
def wait(ctx, timeout=30):
    """
    Wait for the Dockerized database to start-up and respond.

    :param ctx: The context of the invoke task.
    :param timeout: The time in seconds to wait for the database.  Defaults
        to 30 seconds.
    """
    db = rookeries_db.CouchDB.configure_from_env()
    ctx.run(f'dockerize -wait {db.server_url()}/ -timeout {timeout}s')
