import asyncio

from pulsar import when_monitor_start, get_application, task, send
from pulsar.apps.data import create_store
from pulsar.apps.ds import PulsarDS


def start_pulsar_ds(arbiter, host, workers=0):
    lock = getattr(arbiter, 'lock', None)
    if lock is None:
        arbiter.lock = lock = asyncio.Lock()
    yield from lock.acquire()
    try:
        app = yield from get_application('pulsards')
        if not app:
            app = PulsarDS(bind=host, workers=workers, load_config=False)
            cfg = yield from app(arbiter)
        else:
            cfg = app.cfg
        return cfg
    finally:
        lock.release()


@task
def start_store(app, url, workers=0, **kw):
    '''Equivalent to :func:`.create_store` for most cases excepts when the
    ``url`` is for a pulsar store not yet started.
    In this case, a :class:`.PulsarDS` is started.
    '''
    store = create_store(url, **kw)
    if store.name == 'pulsar':
        client = store.client()
        try:
            yield from client.ping()
        except ConnectionRefusedError:
            host = localhost(store._host)
            if not host:
                raise
            cfg = yield from send('arbiter', 'run', start_pulsar_ds,
                                  host, workers)
            store._host = cfg.addresses[0]
            dns = store._buildurl()
            store = create_store(dns, **kw)
    app.cfg.set('data_store', store.dns)


def localhost(host):
    if isinstance(host, tuple):
        if host[0] in ('127.0.0.1', ''):
            return ':'.join((str(b) for b in host))
    else:
        return host


def _start_store(monitor):
    app = monitor.app
    if not isinstance(app, PulsarDS) and app.cfg.data_store:
        start_store(app, app.cfg.data_store)

when_monitor_start.append(_start_store)
