# Path: bts_tools/notification.py
# def send_notification(nodes, node_msg, alert=False):
#     msg = '[%s] %s: %s' % (nodes[0].type(), ', '.join({n.name for n in nodes}), node_msg)
#     if alert:
#         log.warning(msg)
#     else:
#         log.info(msg)
# 
#     for ntype, notify in [('email', send_notification_email),
#                           ('boxcar', send_notification_boxcar),
#                           ('telegram', send_notification_telegram)]:
#         notify_nodes = [node for node in nodes if ntype in node.notification]
#         if notify_nodes:
#             node_names = ', '.join(n.name for n in notify_nodes)
#             msg = '[%s] %s: %s' % (notify_nodes[0].type(), node_names, node_msg)
#             if core.config.get('hostname'):
#                 msg = '[{}] {}'.format(core.config['hostname'], msg)
#             try:
#                 notify(msg, alert)
#             except Exception as e:
#                 log.warning('Failed sending %s notification to %s: %s' % (ntype, node_names, node_msg))
#                 log.exception(e)
# 
# Path: bts_tools/monitor.py
# class StableStateMonitor(object):
#     """Monitor a sequence of states, and is able to compute a "stable" state value when N
#     consecutive same states have happened.
#     For example, we only want to decide the client is offline after 3 consecutive 'offline' states
#     to absorb transient errors and avoid reacting too fast on wrong alerts.
#     """
#     def __init__(self, n):
#         self.n = n
#         self.states = deque(maxlen=n+1)
#         self.last_stable_state = None
# 
#     def push(self, state):
#         stable_state = self.stable_state()
#         if stable_state is not None:
#             self.last_stable_state = stable_state
#         self.states.append(state)
# 
#     def stable_state(self):
#         size = len(self.states)
#         if size < self.n:
#             return None
#         last_state = self.states[-1]
#         if all(state == last_state for state in islice(self.states, size-self.n, size)):
#             return last_state
#         return None
# 
#     def just_changed(self):
#         stable_state = self.stable_state()
#         if stable_state is None or self.last_stable_state is None:
#             return False
#         return stable_state != self.last_stable_state


from ..notification import send_notification
from ..monitor import StableStateMonitor
import logging

log = logging.getLogger(__name__)


def init_ctx(node, ctx, cfg):
    ctx.blockchain_uptodate = StableStateMonitor(3)


def is_valid_node(node):
    return True


def monitor(node, ctx, cfg):
    # TODO: also check delegate participation is low (github #17)

    # if we just came online, force state to not in sync so we get the notification when we're up-to-date
    if ctx.online_state.just_changed():
        for _ in range(3):
            ctx.blockchain_uptodate.push('stale')

    if node.is_synced():
        ctx.blockchain_uptodate.push('up-to-date')

        if ctx.blockchain_uptodate.just_changed():
            send_notification(ctx.nodes, 'blockchain synced and up-to-date')

    else:
        ctx.blockchain_uptodate.push('stale')

        if ctx.blockchain_uptodate.just_changed():

# Path: bts_tools/notification.py
# def send_notification(nodes, node_msg, alert=False):
#     msg = '[%s] %s: %s' % (nodes[0].type(), ', '.join({n.name for n in nodes}), node_msg)
#     if alert:
#         log.warning(msg)
#     else:
#         log.info(msg)
# 
#     for ntype, notify in [('email', send_notification_email),
#                           ('boxcar', send_notification_boxcar),
#                           ('telegram', send_notification_telegram)]:
#         notify_nodes = [node for node in nodes if ntype in node.notification]
#         if notify_nodes:
#             node_names = ', '.join(n.name for n in notify_nodes)
#             msg = '[%s] %s: %s' % (notify_nodes[0].type(), node_names, node_msg)
#             if core.config.get('hostname'):
#                 msg = '[{}] {}'.format(core.config['hostname'], msg)
#             try:
#                 notify(msg, alert)
#             except Exception as e:
#                 log.warning('Failed sending %s notification to %s: %s' % (ntype, node_names, node_msg))
#                 log.exception(e)
# 
# Path: bts_tools/monitor.py
# class StableStateMonitor(object):
#     """Monitor a sequence of states, and is able to compute a "stable" state value when N
#     consecutive same states have happened.
#     For example, we only want to decide the client is offline after 3 consecutive 'offline' states
#     to absorb transient errors and avoid reacting too fast on wrong alerts.
#     """
#     def __init__(self, n):
#         self.n = n
#         self.states = deque(maxlen=n+1)
#         self.last_stable_state = None
# 
#     def push(self, state):
#         stable_state = self.stable_state()
#         if stable_state is not None:
#             self.last_stable_state = stable_state
#         self.states.append(state)
# 
#     def stable_state(self):
#         size = len(self.states)
#         if size < self.n:
#             return None
#         last_state = self.states[-1]
#         if all(state == last_state for state in islice(self.states, size-self.n, size)):
#             return last_state
#         return None
# 
#     def just_changed(self):
#         stable_state = self.stable_state()
#         if stable_state is None or self.last_stable_state is None:
#             return False
#         return stable_state != self.last_stable_state


# Path: bts_tools/monitoring/fork.py
from ..notification import send_notification
from ..monitor import StableStateMonitor
import logging


log = logging.getLogger(__name__)


def init_ctx(node, ctx, cfg):
    ctx.blockchain_uptodate = StableStateMonitor(3)


def is_valid_node(node):
    return True


def monitor(node, ctx, cfg):
    # TODO: also check delegate participation is low (github #17)

    # if we just came online, force state to not in sync so we get the notification when we're up-to-date
    if ctx.online_state.just_changed():
        for _ in range(3):
            ctx.blockchain_uptodate.push('stale')

    if node.is_synced():
        ctx.blockchain_uptodate.push('up-to-date')

        if ctx.blockchain_uptodate.just_changed():
            send_notification(ctx.nodes, 'blockchain synced and up-to-date')

    else:
        ctx.blockchain_uptodate.push('stale')

        if ctx.blockchain_uptodate.just_changed():

            send_notification(ctx.nodes, 'blockchain not in sync anymore', alert=True)