import logging

import ujson.JSONDecodeError

import v20
from v20.errors import V20Timeout, V20ConnectionError
from v20.pricing import ClientPrice, PricingHeartbeat

from algo.config import config
from algo.data.reader import PricePoint
from algo.constant import Symbol
from algo.util import d
from algo.metric import incr_message_counter


log = logging.getLogger(__name__)


def price_to_dict(price: ClientPrice):
    return {
        'type': type(price).__name__,
        'instrument': price.instrument,
        'time': price.time,
        # see main.py for traceback related to this line
        'bid': price.bids[0].price,
        'ask': price.asks[0].price,
        'all-bids': price.bids,
        'all-asks': price.asks,
        'closeoutAsk': price.closeoutAsk,
        'closeoutBid': price.closeoutBid,
        'dict': price.dict()
    }


def heartbeat_to_dict(heartbeat: PricingHeartbeat):
    return {
        'type': heartbeat.type,
        'time': heartbeat.time,
        'dict': heartbeat.dict()
    }


def get_prices_generator(stream):
    for msg_type, msg in stream.parts():
        log.debug(
            'got message', extra={
                'message_type': msg_type,
                'dict': msg.dict()
            }
        )
        incr_message_counter()
        if msg_type == 'pricing.PricingHeartbeat':
            log.debug('pricing.PricingHeartbeat', extra=heartbeat_to_dict(msg))
        elif msg_type == 'pricing.ClientPrice':
            log.debug('pricing.ClientPrice', extra=price_to_dict(msg))
            yield PricePoint.from_oanda_price(msg)
        else:
            log.info('unexpected-stream-message', extra=d(msg))


def get_live_price_stream(symbol: Symbol):
    """
    first in a queue to cover with tests
    """
    api = v20.Context(
        hostname=config.oanda_stream_host_name,
        token=config.oanda_token,
    )
    while True:
        # exit point is unexpected  error
        try:
            log.info('getting-new-stream')
            stream = api.pricing.stream(
                accountID=config.oanda_account_id,
                instruments=symbol.value,
                snapshot=True
            )

            yield from get_prices_generator(stream)
        except (V20ConnectionError, V20Timeout) as err:
            # stream can timeout, so re-connecting
            log.error(
                'catched-v20error',
                extra=d(err, err_type=type(err)),
                exc_info=err
            )
        except ujson.JSONDecodeError as json_decode_error:
            log.error('json-decode-error', extra=d(json_decode_error))

        except Exception as unexpected_error:
            # die bug log why exactly
            log.error(
                'unexpected-error',
                extra=(d(unexpected_error,
                         err_type=type(unexpected_error))),
                exc_info=unexpected_error
            )
            raise
