import logging
from datetime import datetime
from collections import namedtuple

from yandex.maps.pgpool3 import PgPool, PoolConstants


class _TimeFixer(logging.Filter):
    def filter(self, record):
        if 'extra' in record.args:
            extra = record.args['extra']
            if 'ymlog_created' in extra:
                ts = int(extra['ymlog_created'])
                record.created = datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S')
        return True


logging.getLogger('pgpool3').addFilter(_TimeFixer())


# TODO: remove PoolHolder class
PoolHolder = namedtuple('PoolHolder', ['pool'])


def _create_pgpool(master_instance, slave_instances, host_instances, conn_str, role_params, db_id):
    pool_constants = PoolConstants(
        master_size=role_params.write_size,
        master_max_size=role_params.write_size + role_params.write_overflow,
        slave_size=role_params.read_size,
        slave_max_size=role_params.read_size + role_params.read_overflow)

    logger = logging.getLogger('pgpool3.{0}'.format(db_id))
    logger.debug('init')
    logging.getLogger('pgpool3.{0}'.format(db_id))
    pool = None
    if len(host_instances):
        pool = PgPool(host_instances,
                      conn_str, pool_constants, logging.getLogger('pgpool3.{0}.pool'.format(db_id)))
    else:
        pool = PgPool(master_instance, slave_instances,
                      conn_str, pool_constants, logging.getLogger('pgpool3.{0}.pool'.format(db_id)))
    logger.debug('pool created')
    return PoolHolder(pool)


def create_pgpool(db_params, pool_role):
    write_conn = db_params.write_conn
    read_conns = db_params.read_conns
    host_conns = db_params.host_conns
    role_params = db_params.pool_params.roles[pool_role]

    master_instance = (write_conn.host, write_conn.port) if write_conn else None
    slave_instances = [(conn.host, conn.port) for conn in read_conns]
    host_instances = [(conn.host, conn.port) for conn in host_conns]

    # assume that all the hosts have the same dbname, user and password params
    any_conn = write_conn if len(host_conns) == 0 else host_conns[0]

    conn_str = "dbname={dbname} user={user} password={password}".format(
        dbname=any_conn.dbname,
        user=any_conn.user,
        password=any_conn.password)

    return _create_pgpool(master_instance, slave_instances, host_instances, conn_str, role_params, db_params.id)


_pgpool_cache = {}
_pgpool_roles = {}


def init_pgpool(db_params, pool_role):
    db_id = db_params.id
    if db_id in _pgpool_roles:
        if _pgpool_roles[db_id] == pool_role:
            # already initialized
            return None
        else:
            raise RuntimeError(
                'Attempt to reinitialize pgpool for database {db} '
                'having pool role {old_role} with pool role {new_role}'.format(
                    db=db_id, old_role=_pgpool_roles[db_id], new_role=pool_role))

    _pgpool_cache[db_id] = create_pgpool(db_params, pool_role)
    _pgpool_roles[db_id] = pool_role


def get_pgpool(db_id):
    if db_id not in _pgpool_cache:
        raise RuntimeError('pgpool for database {db} has not been initialized yet'.format(db=db_id))
    return _pgpool_cache[db_id].pool
