from realms import config, create_app, db, __version__, cli
from realms.lib.util import random_string, in_virtualenv, green, yellow, red
from subprocess import call, Popen
from multiprocessing import cpu_count
import click
import json
import sys
import os
import pip
import time
import subprocess

# called to discover commands in modules
app = create_app()


def get_user():
    for name in ('SUDO_USER', 'LOGNAME', 'USER', 'LNAME', 'USERNAME'):
        user = os.environ.get(name)
        if user:
            return user


def get_pid():
    try:
        with file(config.PIDFILE) as f:
            return f.read().strip()
    except IOError:
        return None


def is_running(pid):
    if not pid:
        return False

    pid = int(pid)

    try:
        os.kill(pid, 0)
    except OSError:
        return False

    return True


def module_exists(module_name):
    try:
        __import__(module_name)
    except ImportError:
        return False
    else:
        return True


@cli.command()
@click.option('--site-title',
              default=config.SITE_TITLE,
              prompt='Enter site title.')
@click.option('--base_url',
              default=config.BASE_URL,
              prompt='Enter base URL.')
@click.option('--port',
              default=config.PORT,
              prompt='Enter port number.')
@click.option('--secret-key',
              default=config.SECRET_KEY if config.SECRET_KEY != "CHANGE_ME" else random_string(64),
              prompt='Enter secret key.')
@click.option('--wiki-path',
              default=config.WIKI_PATH,
              prompt='Enter wiki data directory.',
              help='Wiki Directory (git repo)')
@click.option('--allow-anon',
              default=config.ALLOW_ANON,
              is_flag=True,
              prompt='Allow anonymous edits?')
@click.option('--registration-enabled',
              default=config.REGISTRATION_ENABLED,
              is_flag=True,
              prompt='Enable registration?')
@click.option('--cache-type',
              default=config.CACHE_TYPE,
              type=click.Choice([None, 'simple', 'redis', 'memcached']),
              prompt='Cache type?')
@click.option('--search-type',
              default=config.CACHE_TYPE,
              type=click.Choice(['simple', 'elasticsearch']),
              prompt='Search type?')
@click.option('--db-uri',
              default=config.DB_URI,
              prompt='Database URI? Examples: http://goo.gl/RyW0cl')
@click.pass_context
def setup(ctx, **kw):
    """ Start setup wizard
    """

    try:
        os.mkdir('/etc/realms-wiki')
    except OSError:
        pass

    conf = {}

    for k, v in kw.items():
        conf[k.upper()] = v

    conf_path = config.update(conf)

    if conf['CACHE_TYPE'] == 'redis':
        ctx.invoke(setup_redis)
    elif conf['CACHE_TYPE'] == 'memcached':
        ctx.invoke(setup_memcached)

    if conf['SEARCH_TYPE'] == 'elasticsearch':
        ctx.invoke(setup_elasticsearch)

    green('Config saved to %s' % conf_path)

    if not conf_path.startswith('/etc/realms-wiki'):
        yellow('Note: You can move file to /etc/realms-wiki/realms-wiki.json')
        click.echo()

    yellow('Type "realms-wiki start" to start server')
    yellow('Type "realms-wiki dev" to start server in development mode')
    yellow('Full usage: realms-wiki --help')


@click.command()
@click.option('--cache-redis-host',
              default=getattr(config, 'CACHE_REDIS_HOST', "127.0.0.1"),
              prompt='Redis host')
@click.option('--cache-redis-port',
              default=getattr(config, 'CACHE_REDIS_POST', 6379),
              prompt='Redis port')
@click.option('--cache-redis-password',
              default=getattr(config, 'CACHE_REDIS_PASSWORD', None),
              prompt='Redis password')
@click.option('--cache-redis-db',
              default=getattr(config, 'CACHE_REDIS_DB', 0),
              prompt='Redis db')
def setup_redis(**kw):
    conf = {}

    for k, v in kw.items():
        conf[k.upper()] = v

    config.update(conf)
    install_redis()


@click.command()
@click.option('--elasticsearch-url',
              default=getattr(config, 'ELASTICSEARCH_URL', 'http://127.0.0.1:9200'),
              prompt='Elasticsearch URL')
def setup_elasticsearch(**kw):
    conf = {}

    for k, v in kw.items():
        conf[k.upper()] = v

    config.update(conf)


def get_prefix():
    return sys.prefix


@cli.command(name='pip')
@click.argument('cmd', nargs=-1)
def pip_(cmd):
    """ Execute pip commands, useful for virtualenvs
    """
    pip.main(cmd)


def install_redis():
    pip.main(['install', 'redis'])


def install_mysql():
    pip.main(['install', 'MySQL-Python'])


def install_postgres():
    pip.main(['install', 'psycopg2'])


def install_crate():
    pip.main(['install', 'crate'])


def install_memcached():
    pip.main(['install', 'python-memcached'])


@click.command()
@click.option('--cache-memcached-servers',
              default=getattr(config, 'CACHE_MEMCACHED_SERVERS', ["127.0.0.1:11211"]),
              type=click.STRING,
              prompt='Memcached servers, separate with a space')
def setup_memcached(**kw):
    conf = {}

    for k, v in kw.items():
        conf[k.upper()] = v

    config.update(conf)


@cli.command()
@click.option('--user',
              default=get_user(),
              type=click.STRING,
              prompt='Run as which user? (it must exist)')
@click.option('--port',
              default=config.PORT,
              type=click.INT,
              prompt='What port to listen on?')
@click.option('--workers',
              default=cpu_count() * 2 + 1,
              type=click.INT,
              prompt="Number of workers? (defaults to ncpu*2+1)")
def setup_upstart(**kwargs):
    """ Start upstart conf creation wizard
    """
    from realms.lib.util import upstart_script

    if in_virtualenv():
        app_dir = get_prefix()
        path = '/'.join(sys.executable.split('/')[:-1])
    else:
        # Assumed root install, not sure if this matters?
        app_dir = '/'
        path = None

    kwargs.update(dict(app_dir=app_dir, path=path))

    conf_file = '/etc/init/realms-wiki.conf'
    script = upstart_script(**kwargs)

    try:
        with open(conf_file, 'w') as f:
            f.write(script)
        green('Wrote file to %s' % conf_file)
    except IOError:
        with open('/tmp/realms-wiki.conf', 'w') as f:
            f.write(script)
        yellow("Wrote file to /tmp/realms-wiki.conf, to install type:")
        yellow("sudo mv /tmp/realms-wiki.conf /etc/init/realms-wiki.conf")

    click.echo()
    click.echo("Upstart usage:")
    green("sudo start realms-wiki")
    green("sudo stop realms-wiki")
    green("sudo restart realms-wiki")
    green("sudo status realms-wiki")


@cli.command()
@click.argument('json_string')
def configure(json_string):
    """ Set config, expects JSON encoded string
    """
    try:
        config.update(json.loads(json_string))
    except ValueError, e:
        red('Config value should be valid JSON')


@cli.command()
@click.option('--port', default=config.PORT)
def dev(port):
    """ Run development server
    """
    green("Starting development server")

    config_path = config.get_path()
    if config_path:
        green("Using config: %s" % config_path)
    else:
        yellow("Using default configuration")

    create_app().run(host="0.0.0.0",
                     port=port,
                     debug=True)


def start_server():
    if is_running(get_pid()):
        yellow("Server is already running")
        return

    flags = '--daemon --pid %s' % config.PIDFILE

    green("Server started. Port: %s" % config.PORT)

    config_path = config.get_path()
    if config_path:
        green("Using config: %s" % config_path)
    else:
        yellow("Using default configuration")

    Popen("gunicorn 'realms:create_app()' -b 0.0.0.0:%s -k gevent %s" %
          (config.PORT, flags), shell=True, executable='/bin/bash')


def stop_server():
    pid = get_pid()
    if not is_running(pid):
        yellow("Server is not running")
    else:
        yellow("Shutting down server")
        call(['kill', pid])
        while is_running(pid):
            time.sleep(1)


@cli.command()
def run():
    """ Run production server (alias for start)
    """
    start_server()


@cli.command()
def start():
    """ Run server daemon
    """
    start_server()


@cli.command()
def stop():
    """ Stop server
    """
    stop_server()


@cli.command()
def restart():
    """ Restart server
    """
    stop_server()
    start_server()


@cli.command()
def status():
    """ Get server status
    """
    pid = get_pid()
    if not is_running(pid):
        yellow("Server is not running")
    else:
        green("Server is running PID: %s" % pid)


@cli.command()
def create_db():
    """ Creates DB tables
    """
    green("Creating all tables")
    with app.app_context():
        green('DB_URI: %s' % app.config.get('DB_URI'))
        db.metadata.create_all(db.get_engine(app))


@cli.command()
@click.confirmation_option(help='Are you sure you want to drop the db?')
def drop_db():
    """ Drops DB tables
    """
    yellow("Dropping all tables")
    with app.app_context():
        db.metadata.drop_all(db.get_engine(app))


@cli.command()
def test():
    """ Run tests
    """
    for mod in [('flask.ext.testing', 'Flask-Testing'), ('nose', 'nose'), ('blinker', 'blinker')]:
        if not module_exists(mod[0]):
            pip.main(['install', mod[1]])

    nosetests = get_prefix() + "/bin/nosetests" if in_virtualenv() else "nosetests"

    call([nosetests, 'realms'])


@cli.command()
def version():
    """ Output version
    """
    green(__version__)


@cli.command(add_help_option=False)
def deploy():
    """ Deploy to PyPI and docker hub
    """
    call("python setup.py sdist upload", shell=True)
    call("sudo docker build --no-cache -t realms/realms-wiki %s/docker" % app.config['APP_PATH'], shell=True)
    id_ = json.loads(Popen("sudo docker inspect realms/realms-wiki".split(), stdout=subprocess.PIPE).communicate()[0])[0]['Id']
    call("sudo docker tag %s realms/realms-wiki:%s" % (id_, __version__), shell=True)
    call("sudo docker push realms/realms-wiki", shell=True)

if __name__ == '__main__':
    cli()
