"""Mailur CLI

Usage:
  mlr remote <login> set <username> <password> --imap=<host> --smtp=<host>
  mlr remote <login> [--tag=<tag> --box=<box> --parse] [options]
  mlr parse <login> [<criteria>] [options]
  mlr metadata <login> [<uids>]
  mlr sync <login> [--timeout=<timeout>]
  mlr sync-flags <login> [--reverse]
  mlr clean-flags <login> <flag>...
  mlr icons
  mlr web
  mlr lint [--ci]
  mlr test
  mlr -h | --help
  mlr --version

Options:
  -b <batch>    Batch size [default: 1000].
  -t <threads>  Amount of threads for thread pool [default: 2].
"""
import functools as ft
import pathlib
import sys
import time

from docopt import docopt
from gevent import joinall, sleep, spawn

from . import conf, local, log, remote

root = pathlib.Path(__file__).resolve().parent.parent


def main(args=None):
    if args is None:
        args = sys.argv[1:]
    args = docopt(__doc__, args, version='Mailur 0.3')
    try:
        process(args)
    except KeyboardInterrupt:
        raise SystemExit('^C')


def process(args):
    conf['USER'] = args['<login>']
    opts = {
        'batch': int(args.get('-b')),
        'threads': int(args.get('-t')),
    }
    if args['remote'] and args['set']:
        remote.data_account({
            'username': args['<username>'],
            'password': args['<password>'],
            'imap_host': args['--imap'],
            'smtp_host': args['--smtp'],
        })
    elif args['remote']:
        select_opts = dict(tag=args['--tag'], box=args['--box'])
        fetch_opts = dict(opts, **select_opts)
        fetch_opts = {k: v for k, v in fetch_opts.items() if v}

        remote.fetch(**fetch_opts)
        if args['--parse']:
            local.parse(**opts)
    elif args['parse']:
        local.parse(args.get('<criteria>'), **opts)
    elif args['sync']:
        timeout = args.get('--timeout')
        params = [int(timeout)] if timeout else []
        sync(*params)
    elif args['sync-flags']:
        if args['--reverse']:
            local.sync_flags_to_src()
        else:
            local.sync_flags_to_all()
    elif args['clean-flags']:
        local.clean_flags(args['<flag>'])
    elif args['metadata']:
        local.update_metadata(args.get('<uids>'))
    elif args['icons']:
        icons()
    elif args['web']:
        web()
    elif args['test']:
        run('pytest -q --cov=mailur --cov-report=term-missing')
    elif args['lint']:
        ci = args['--ci'] and 1 or ''
        run('ci=%s bin/run-lint' % ci)
    else:
        raise SystemExit('Target not defined:\n%s' % args)


def run_forever(fn):
    # but if it always raises exception, run only 3 times

    @ft.wraps(fn)
    def inner(*a, **kw):
        count = 3
        while count:
            try:
                fn(*a, **kw)
            except Exception as e:
                log.exception(e)
                sleep(10)
                count = -1
    return inner


def sync(timeout=1200):
    @run_forever
    def idle_remote(params):
        with remote.client(**params) as c:
            handlers = {
                'EXISTS': lambda res: remote.sync(),
                'FETCH': lambda res: remote.sync(only_flags=True),
            }
            c.idle(handlers, timeout=timeout)

    @run_forever
    def sync_flags():
        local.sync_flags_to_all()
        local.sync_flags(
            post_handler=lambda res: remote.sync(only_flags=True),
            timeout=timeout
        )

    try:
        remote.sync()
        jobs = [spawn(sync_flags)]
        for params in remote.get_folders():
            jobs.append(spawn(idle_remote, params))
        joinall(jobs, raise_error=True)
    except KeyboardInterrupt:
        time.sleep(1)


def web():
    from gevent.subprocess import run
    from gevent.pool import Pool

    def api():
        run('bin/run-web', shell=True)

    def webpack():
        run('command -v yarn && yarn run dev || npm run dev', shell=True)

    try:
        pool = Pool()
        pool.spawn(api)
        pool.spawn(webpack)
        pool.join()
    except KeyboardInterrupt:
        time.sleep(1)


def run(cmd):
    from sys import exit
    from subprocess import call

    check = 'command -v pytest'
    if call(check, cwd=root, shell=True):
        raise SystemExit(
            'Test dependencies must be installed.\n'
            '$ pip install -e .[test]'
        )

    cmd = 'sh -xc %r' % cmd
    exit(call(cmd, cwd=root, shell=True))


def icons():
    import json
    import bottle

    font = root / 'assets/font'
    sel = (font / 'selection.json').read_text()
    sel = json.loads(sel)
    sel_pretty = json.dumps(sel, indent=2, ensure_ascii=False, sort_keys=True)
    (font / 'selection.json').write_text(sel_pretty)
    icons = [
        (i['properties']['name'], '\\%s' % hex(i['properties']['code'])[2:])
        for i in sel['icons']
    ]
    tpl = str((font / 'icons.less.tpl').resolve())
    txt = bottle.template(
        tpl, icons=icons,
        template_settings={'syntax': '{% %} % {{ }}'}
    )
    f = font / 'icons.less'
    f.write_text(txt)
    print('%s updated' % f)


if __name__ == '__main__':
    main()