"""
Fake model used for testing and verification.
"""
import asyncio
import logging
from .grpc_metrics_model import FlexMetric


def _print_flex_batch(flexes):
    """default handler function"""
    from tabulate import tabulate
    print(f'{len(flexes)} Flex Metrics')
    if flexes:
        print(tabulate(flexes, flexes[0]._fields))


class FakeNetworkMetricsModel(object):
    """Fake metrics client.  You can pass in a callback that will be called
       with each metric batch that would be output to the metrics server by
       the real model.  By default it just prints them out."""

    def __init__(self, cbk=None):
        self._flex_q = asyncio.Queue(5000)  # same size queue as real impl
        self._flex_task = None
        self.cbk = cbk or _print_flex_batch
        self.drop_count = 0

    async def close(self):
        if self._flex_task:
            self._flex_task.cancel()
            self._flex_task = None

    async def _service_flex_queue(self):
        """Service the flex queue"""
        n = 0
        try:
            while True:
                flexes = await self._flex_q.get()
                n += 1
                try:
                    flexes.extend(self._flex_q.get_nowait())
                    n += 1
                except asyncio.QueueEmpty:
                    pass
                try:
                    if asyncio.iscoroutinefunction(self.cbk):
                        await self.cbk(flexes)
                    else:
                        self.cbk(flexes)
                except Exception as err:
                    logging.error('Error pushing flexes: %s', err)
                finally:
                    for _ in range(n):
                        self._flex_q.task_done()
                    n = 0
        except asyncio.CancelledError:  # typical case
            pass
        except Exception as e:
            logging.info('Exiting _service_flex_queue due to %s', e)

    async def push_flexes(self, flexes):
        """Push flex metrics."""
        if not flexes:
            return
        assert isinstance(flexes, list)
        if not self._flex_task:
            self._flex_task = asyncio.get_event_loop().create_task(
                self._service_flex_queue())
        try:
            self._flex_q.put_nowait(flexes)
        except asyncio.QueueFull:  # send queue is full, drop the request
            self.drop_count += len(flexes)
