# -*- coding: utf-8; -*-
"""
Fabric script for a server running Theo, the order system

Please see the accompanying README for full instructions.
"""

import datetime

from fabric2 import task

from rattail.core import Object
from rattail_fabric2 import make_deploy, apt, mysql, postfix, postgresql, exists, make_system_user, mkdir


env = Object()
deploy = make_deploy(__file__)


##############################
# bootstrap
##############################

@task
def bootstrap_all(c):
    """
    Bootstrap all aspects of the machine
    """
    bootstrap_base(c)
    if env.theo_install_production:
        bootstrap_theo(c)
    bootstrap_theo_stage(c)


@task
def bootstrap_base(c):
    """
    Bootstrap the base system
    """
    # we do `apt update && apt dist-upgrade` every time this task runs
    apt.dist_upgrade(c)

    # we assume postfix is installed, but the configuration thereof is not
    # dealt with here; you may need to configure further on your own
    postfix.install(c)

    # rattail user + common config
    make_system_user(c, 'rattail', home='/var/lib/rattail', shell='/bin/bash')
    postfix.alias(c, 'rattail', 'root')
    mkdir(c, '/etc/rattail', use_sudo=True)
    deploy(c, 'rattail/rattail.conf.mako', '/etc/rattail/rattail.conf',
           use_sudo=True, context={'env': env})

    # postgresql
    postgresql.install(c)
    postgresql.create_user(c, 'rattail', password=env.password_postgresql_rattail)

    # mysql (maybe)
    if env.theo_mirror_posdb == 'mysql':
        mysql.install(c)
        mysql.create_user(c, 'rattail', password=env.password_mysql_rattail)

    # python
    mkdir(c, '/srv/envs', use_sudo=True, owner='rattail:')
    apt.install(
        c,
        'build-essential',
        'git',
        'libpq-dev',
        'python3-dev',
        'python3-venv',
        'supervisor',
    )

    # catapult extras
    if env.theo_integrates_with == 'catapult':

        # freetds / odbc
        apt.install(c, 'unixodbc', 'unixodbc-dev')
        # we must install FreeTDS from source, b/c the version APT makes available
        # is too old.  however the *latest* source seems to have some issue(s)
        # which cause it to use too much memory, so we use a more stable branch
        from rattail_fabric2 import freetds
        freetds.install_from_source(c, user='rattail', branch='Branch-1_2')
        deploy(c, 'rattail/catapult/freetds.conf.mako', '/usr/local/etc/freetds.conf',
               use_sudo=True, context={'env': env})
        deploy(c, 'rattail/catapult/odbc.ini', '/etc/odbc.ini', use_sudo=True)

    # locsms extras
    elif env.theo_integrates_with == 'locsms':
        apt.install(c, 'unixodbc', 'unixodbc-dev')

        # mssql odbc
        if env.locsms_odbc_driver == 'mssql':
            from rattail_fabric2 import mssql
            mssql.install_mssql_odbc(c, accept_eula=env.mssql_driver_accept_eula)
            deploy(c, 'rattail/locsms/mssql/odbc.ini.mako', '/etc/odbc.ini',
                   use_sudo=True, context={'env': env})

        else: # freetds odbc
            apt.install(c, 'tdsodbc')
            deploy(c, 'rattail/locsms/freetds/freetds.conf.mako', '/etc/freetds/freetds.conf',
                   use_sudo=True, context={'env': env})
            deploy(c, 'rattail/locsms/freetds/odbc.ini.mako', '/etc/odbc.ini',
                   use_sudo=True, context={'env': env})


@task
def bootstrap_theo(c):
    """
    Bootstrap Theo, the order system
    """
    install_theo_app(c, 'theo', True, 7000,
                     # overnight runs at 1:00am
                     run_overnight_at=datetime.time(1))


@task
def bootstrap_theo_stage(c):
    """
    Bootstrap "stage" for Theo, the order system
    """
    install_theo_app(c, 'theo-stage', False, 7010, from_source=True,
                     # overnight runs at 12:30am
                     run_overnight_at=datetime.time(0, 30))


##############################
# support functions
##############################

def install_theo_app(c, envname, production, port, from_source=False,
                     run_overnight_at=None):
    """
    Install a Theo app, per the given parameters
    """
    dbname = envname
    safename = envname.replace('-', '_')
    envroot = '/srv/envs/{}'.format(envname)

    mirrordb = None
    if env.theo_integrates_with == 'catapult':
        mirrordb = 'catapult-mirror' if production else 'catapult-mirror-stage'

    c.sudo('supervisorctl stop {}:'.format(safename), warn=True)

    # virtualenv
    if not exists(c, envroot):
        c.sudo('python3 -m venv {}'.format(envroot), user='rattail')
        c.sudo('{}/bin/pip install --upgrade pip wheel'.format(envroot), user='rattail')
    deploy(c, 'theo-common/pip.conf.mako', '{}/pip.conf'.format(envroot),
           use_sudo=True, owner='rattail:', mode='0600',
           context={'env': env, 'envroot': envroot})
    c.sudo('touch {}/pip.log'.format(envroot), user='rattail')
    c.sudo('chmod 0600 {}/pip.log'.format(envroot))
    c.sudo('PIP_CONFIG_FILE={0}/pip.conf {0}/bin/pip install ipdb'.format(envroot),
           user='rattail')

    # theo
    if from_source:
        install_theo_source(c, envroot)
    # always install theo package by name, even if running from source
    if env.theo_integrates_with == 'corepos':
        pkgname = 'tailbone-theo[app,corepos]'
    elif env.theo_integrates_with == 'catapult':
        pkgname = 'tailbone-theo[app,catapult]'
    elif env.theo_integrates_with == 'locsms':
        pkgname = 'tailbone-theo[app,locsms]'
    else:
        pkgname = 'tailbone-theo[app]'
    c.sudo("bash -c 'PIP_CONFIG_FILE={0}/pip.conf cd {0} && bin/pip install {1}'".format(envroot, pkgname),
           user='rattail')

    # app dir
    if not exists(c, '{}/app'.format(envroot)):
        c.sudo("bash -c 'cd {} && bin/rattail make-appdir'".format(envroot),
               user='rattail')
    c.sudo('chmod 0750 {}/app/log'.format(envroot))

    # config
    deploy(c, 'theo-common/rattail.conf.mako', '{}/app/rattail.conf'.format(envroot),
           use_sudo=True, owner='rattail:', mode='0600',
           context={'env': env, 'envroot': envroot, 'dbname': dbname,
                    'production': production, 'mirrordb': mirrordb})
    if not exists(c, '{}/app/quiet.conf'.format(envroot)):
        c.sudo("bash -c 'cd {} && bin/rattail make-config -T quiet -O app/'".format(envroot),
               user='rattail')
    deploy(c, 'theo-common/web.conf.mako', '{}/app/web.conf'.format(envroot),
           use_sudo=True, owner='rattail:', mode='0600',
           context={'env': env, 'envroot': envroot, 'dbname': dbname, 'port': port})
    deploy(c, 'theo-common/cron.conf.mako', '{}/app/cron.conf'.format(envroot),
           use_sudo=True, owner='rattail:',
           context={'envroot': envroot})
    if env.theo_mirror_posdb:
        if env.theo_integrates_with == 'catapult':
            deploy(c, 'theo-common/catapult-mirror.conf', '{}/app/catapult-mirror.conf'.format(envroot),
                   use_sudo=True, owner='rattail:')

    # scripts
    deploy(c, 'theo-common/upgrade.sh.mako', '{}/app/upgrade.sh'.format(envroot),
           use_sudo=True, owner='rattail:', mode='0755',
           context={'env': env, 'envroot': envroot, 'production': production})
    deploy(c, 'theo-common/tasks.py.mako', '{}/app/tasks.py'.format(envroot),
           use_sudo=True, owner='rattail:',
           context={'envroot': envroot})
    deploy(c, 'theo-common/upgrade-wrapper.sh.mako', '{}/app/upgrade-wrapper.sh'.format(envroot),
           use_sudo=True, owner='rattail:', mode='0755',
           context={'envroot': envroot, 'safename': safename})
    deploy(c, 'theo-common/overnight.sh.mako', '{}/app/overnight.sh'.format(envroot),
           use_sudo=True, owner='rattail:', mode='0755',
           context={'env': env, 'envroot': envroot})
    if env.theo_integrates_with == 'corepos':
        deploy(c, 'theo-common/import-corepos-first.sh.mako', '{}/app/import-corepos-first.sh'.format(envroot),
               use_sudo=True, owner='rattail:', mode='0755',
               context={'envroot': envroot})
    elif env.theo_integrates_with == 'catapult':
        deploy(c, 'theo-common/import-catapult-first.sh.mako', '{}/app/import-catapult-first.sh'.format(envroot),
               use_sudo=True, owner='rattail:', mode='0755',
               context={'envroot': envroot})
        if env.theo_mirror_posdb:
            deploy(c, 'theo-common/mirror-catapult-first.sh.mako', '{}/app/mirror-catapult-first.sh'.format(envroot),
                   use_sudo=True, owner='rattail:', mode='0755',
                   context={'envroot': envroot})
    elif env.theo_integrates_with == 'locsms':
        deploy(c, 'theo-common/import-locsms-first.sh.mako', '{}/app/import-locsms-first.sh'.format(envroot),
               use_sudo=True, owner='rattail:', mode='0755',
               context={'envroot': envroot})

    # theo db
    if not postgresql.db_exists(c, dbname):
        postgresql.create_db(c, dbname, owner='rattail', checkfirst=False)
        c.sudo("bash -c 'cd {} && bin/alembic -c app/rattail.conf upgrade heads'".format(envroot),
               user='rattail')

        # misc. default settings
        postgresql.sql(c, "insert into setting values ('tailbone.theme', 'falafel')",
                       database=dbname)
        postgresql.sql(c, "insert into setting values ('tailbone.themes.expose_picker', 'false')",
                       database=dbname)
        postgresql.sql(c, "insert into setting values ('tailbone.global_help_url', 'https://rattailproject.org/moin/Documentation')",
                       database=dbname)

        # theo user
        c.sudo("bash -c 'cd {} && bin/rattail -c app/quiet.conf --no-versioning make-user theo --no-password'".format(envroot),
               user='rattail')
        c.sudo("bash -c 'cd {} && bin/rattail -c app/quiet.conf --runas theo import-versions User'".format(envroot),
               user='rattail')

        # admin user
        c.sudo("bash -c 'cd {} && bin/rattail -c app/quiet.conf --runas theo make-user --admin {} --password {}'".format(
            envroot, env.theo_admin_username, env.theo_admin_password),
               user='rattail', echo=False)

        # integration users
        if env.theo_integrates_with == 'corepos':
            c.sudo("bash -c 'cd {} && bin/rattail -c app/quiet.conf --runas theo make-user --no-password corepos'".format(envroot),
                   user='rattail')
        elif env.theo_integrates_with == 'catapult':
            c.sudo("bash -c 'cd {} && bin/rattail -c app/quiet.conf --runas theo make-user --no-password catapult'".format(envroot),
                   user='rattail')
        elif env.theo_integrates_with == 'locsms':
            c.sudo("bash -c 'cd {} && bin/rattail -c app/quiet.conf --runas theo make-user --no-password locsms'".format(envroot),
                   user='rattail')

        # tweaks for mirror DB
        if env.theo_mirror_posdb and env.theo_integrates_with == 'catapult':
            postgresql.sql(c, "insert into setting values ('tailbone.engines.catapult.pretend_default', 'mirror')",
                           database=dbname)
            postgresql.sql(c, "insert into setting values ('rattail_onager.catapult.importing.db.default_models', 'VersionInfo Store Department Vendor Brand FamilyLine ComboGroupProfile WeightProfile PricingLevelInfo DiscountProfile StockInventory PricingLevelDetail Prefix Suffix DiscountGroup Customer Employee PhoneDescription Phone EmailDescription Email AddressDescription Address')",
                           database=dbname)
            postgresql.sql(c, "insert into setting values ('rattail.mail.rattail_onager_import_catapult_updates.subject', 'Changes for Catapult (production) -> Catapult (mirror)')",
                           database=dbname)

    # maybe establish the mirror DB
    if env.theo_mirror_posdb == 'mysql':
        if env.theo_integrates_with == 'catapult':
            if not mysql.db_exists(c, mirrordb):
                mysql.create_db(c, mirrordb, checkfirst=False, user='rattail@localhost')
                c.sudo("bash -c 'cd {} && bin/alembic -c app/catapult-mirror.conf upgrade heads'".format(envroot),
                       user='rattail')

    # supervisor
    deploy(c, 'theo-common/supervisor.conf.mako', '/etc/supervisor/conf.d/{}.conf'.format(safename),
           use_sudo=True, context={'envroot': envroot, 'safename': safename})
    c.sudo('supervisorctl update')
    c.sudo('supervisorctl start {}:'.format(safename))

    # sudoers, crontab, logrotate
    deploy.sudoers(c, 'theo-common/sudoers.mako', '/etc/sudoers.d/{}'.format(safename),
                   context={'envname': envname, 'envroot': envroot})
    deploy(c, 'theo-common/crontab.mako', '/etc/cron.d/{}'.format(safename),
           use_sudo=True,
           context={'env': env, 'envname': envname, 'envroot': envroot,
                    'pretty_time': run_overnight_at.strftime('%I:%M %p'),
                    'cron_time': run_overnight_at.strftime('%M %H')})
    deploy(c, 'theo-common/logrotate.conf.mako', '/etc/logrotate.d/{}'.format(safename),
           use_sudo=True, context={'envroot': envroot})


def install_theo_source(c, envroot):
    """
    Install source code for Theo
    """
    # rattail
    install_source_package(c, envroot, 'rattail')
    install_source_package(c, envroot, 'tailbone')

    # corepos
    if env.theo_integrates_with == 'corepos':
        install_source_package(c, envroot, 'pycorepos')
        install_source_package(c, envroot, 'rattail-corepos')
        install_source_package(c, envroot, 'tailbone-corepos')

    # catapult
    elif env.theo_integrates_with == 'catapult':
        install_source_package(c, envroot, 'onager', restricted=True)
        install_source_package(c, envroot, 'rattail-onager', restricted=True)
        install_source_package(c, envroot, 'tailbone-onager', restricted=True)

    # locsms
    elif env.theo_integrates_with == 'locsms':
        install_source_package(c, envroot, 'luckysmores', restricted=True)
        install_source_package(c, envroot, 'rattail-luckysmores', restricted=True)
        install_source_package(c, envroot, 'tailbone-locsms', restricted=True)

    # theo
    install_source_package(c, envroot, 'theo')


def install_source_package(c, envroot, name, restricted=False):
    if not exists(c, '{}/src/{}'.format(envroot, name)):
        if restricted:
            c.sudo('git clone https://{0}:{1}@kallithea.rattailproject.org/rattail-restricted/{2} {3}/src/{2}'.format(
                env.kallithea_username, env.kallithea_password, name, envroot),
                   user='rattail', echo=False)
        else:
            c.sudo('git clone https://kallithea.rattailproject.org/rattail-project/{0} {1}/src/{0}'.format(name, envroot),
                   user='rattail')
        c.sudo("bash -c 'PIP_CONFIG_FILE={0}/pip.conf cd {0} && bin/pip install -e src/{1}'".format(envroot, name),
               user='rattail')


##############################
# fabenv
##############################

try:
    import fabenv
except ImportError as error:
    print("\ncouldn't import fabenv: {}".format(error))

env.setdefault('machine_is_live', False)