from vxci.common import check_env_vars
from docker import errors, from_env
from os import environ
import re
import subprocess
import shlex
import sys
import psycopg2
import shutil
import time
import click
import logging


_logger = logging.getLogger('vxci.' + __name__)  # pylint: disable=C0103
_cli = from_env().api


def generate_image_name(name):
    """ Generate the base image name using the ref name but cleaning it before,
    ATM only removes "." and "#" from the title to avoid issues with docker naming
    convention """
    res = re.sub(r'[\.#\$\=\+\;\>\,\<,\&\%]', '', name)
    res = re.sub(r'-_', '_', res)
    return res.lower()


def postgres_container(base_name):
    return 'postgres{0}_{1}'.format(base_name, environ['CI_PIPELINE_ID'])


def configure_postgres(base_name, postgres_image):
    name = postgres_container(base_name)
    _logger.info('Pulling the postgres image')
    _cli.pull(postgres_image)
    _logger.info('Creating container %s', name)
    container = _cli.create_container(name=name,
                                      image=postgres_image,
                                      environment={'POSTGRES_PASSWORD': 'postgres'})
    _cli.start(container=container.get('Id'))
    time.sleep(20)
    container_info = _cli.inspect_container(container.get('Id'))
    postgres_host = container_info.get('NetworkSettings').get('IPAddress')
    environ['PGHOST'] = postgres_host
    environ['PGPASSWORD'] = 'postgres'


def configure_postgres_user():
    _logger.info('Creating user docker')
    retry = 0
    while True:
        try:
            with psycopg2.connect(user='postgres') as conn:
                cursor = conn.cursor()
                cursor.execute(
                    "create user docker password 'docker' createdb;"
                )
                break
        except psycopg2.OperationalError as error:
            if 'could not connect to server' in error and retry <= 3:
                retry += 1
                _logger.error('Failed to connect to the database, retrying %s', retry)
                time.sleep(4)
            else:
                _logger.error(error)
                return False
    environ['PGPASSWORD'] = 'docker'
    environ['PGUSER'] = 'docker'
    return True


def run_tests():
    _logger.info('Copying files required for the tests')
    shutil.copy('tests/files/config.json.travis', 'tests/files/config.json')
    _logger.info('Running tests')
    try:
        subprocess.check_call(shlex.split('coverage run setup.py test'))
        subprocess.check_call(shlex.split('coverage report -m'))
        subprocess.check_call(shlex.split('tox -e doc'))
    except subprocess.CalledProcessError:
        return False
    return True


def cleanup(base_name):
    containers = _cli.containers(all=True)
    for container in containers:
        for name in container.get('Names'):
            if base_name in name:
                try:
                    _cli.remove_container(container, force=True)
                except errors.NotFound:
                    _logger.error('Container already deleted')


@click.command()
@click.option('--ci_pipeline_id', default=environ.get('CI_PIPELINE_ID'),
              help=("The unique id of the current pipeline that GitLab CI"
                    " uses internally. Env var: CI_PIPELINE_ID."))
@click.option('--ci_commit_ref_name', default=environ.get('CI_COMMIT_REF_NAME'),
              help=("The branch or tag name for which project is built."
                    " Env var: CI_COMMIT_REF_NAME."))
@click.option('--ci_job_id', default=environ.get('CI_JOB_ID'),
              help=("The unique id of the current job that GitLab CI uses internally."
                    " Env var: CI_JOB_ID."))
@click.option('--psql_image', default=False,
              help=("Override the default postgresql image to use for the tests"
                    "(Notice that this will override the PSQL_VERSION too)"))
def deployv_tests(**kwargs):
    check_env_vars(**kwargs)
    base_name = generate_image_name('{0}_{1}'.format(
        kwargs.get('ci_commit_ref_name'), kwargs.get('ci_job_id')))
    if kwargs.get('psql_image'):
        postgres_image = kwargs.get('psql_image')
    else:
        postgres_image = 'postgres:{0}'.format(environ.get('PSQL_VERSION', '9.6'))
    cleanup(base_name)
    configure_postgres(base_name, postgres_image)
    res = configure_postgres_user()
    if not res:
        cleanup(base_name)
        sys.exit(1)
    res = run_tests()
    cleanup(base_name)
    if not res:
        sys.exit(1)
    sys.exit(0)
