import logging
from datetime import datetime, timedelta
from v20.trade import Trade

from algo.constant import Symbol
from algo.trading import OrderType, TradeResult
from algo.data.common import PricePoint
from algo.trading.oanda import RestApi
from algo.external.trainer import TrainerApi

log = logging.getLogger(__name__)


class TimelyReport:
    REPORT_ON_TICK_EVERY = timedelta(seconds=60)

    def __init__(self) -> None:
        self._last_report_time = datetime.now()

    def is_time_to_report(self):
        return (datetime.now() - self._last_report_time) > self.REPORT_ON_TICK_EVERY

    def report(self, price: PricePoint):
        """
        subclass suppose to reimplement method
        """
        log.info('tick', extra={'p': price})

    def tick(self, price: PricePoint):
        if self.is_time_to_report():
            self.report(price)
            self._last_report_time = datetime.now()
            return True
        return False


class CurrentState(TimelyReport):
    def __init__(self) -> None:
        super().__init__()
        self._in_market = False
        self._open_price: float = 0
        self._order_type: OrderType = None
        self._sl_size: float
        self.is_sl_close: bool = False

    @property
    def in_market(self) -> bool:
        return self._in_market

    @property
    def order_type(self) -> OrderType:
        return self._order_type

    def open(self, price_point: PricePoint, order_type: OrderType, sl_size=None):
        """ sl_size set as negative value to easy further computation"""
        self._in_market = True
        self._order_type = order_type
        self._open_price = price_point.bid
        if sl_size is not None:
            self._sl_size = - 1.0 * sl_size

    def is_sl_time(self, price_point: PricePoint):
        """ SL is negative value"""
        return self.current_profit(price_point) < self._sl_size

    def current_profit(self, price_point: PricePoint) -> float:
        profit = self._open_price - price_point.bid
        if self._order_type != OrderType.SELL:
            profit *= -1
        return profit

    def close(self, price_point: PricePoint) -> TradeResult:
        """ close the trade and return profit"""
        self._in_market = False
        return TradeResult(
            self.current_profit(price_point),
            price_point.ask - price_point.bid
        )

    def __str__(self) -> str:
        return f'<State: order: {self.order_type}, open: {self._open_price}>'


class OandaTradingState(CurrentState):
    def __init__(self, default_symbol: Symbol) -> None:
        super().__init__()
        self._api = RestApi(default_symbol)
        # closing all trades for the case when prev run was interrupted
        self._api.close_all_trades()
        self._trainer_api = TrainerApi()

    @staticmethod
    def _trade_summary(trade: Trade, price: PricePoint):
        profit = trade.currentUnits / abs(trade.initialUnits) * (price.ask - trade.price)
        return {
            'profit': profit,
            'pprofit': profit * (10 ** 2)
        }

    def debug_state(self, price: PricePoint):
        open_trades = self._api.get_open_trades()
        log.info('current-trades', extra={'trades': [trade.dict() for trade in open_trades]})

        log.info('price-diff', extra={'trades': [self._trade_summary(trade, price) for trade in open_trades]})
        # open_positions = self._api.get_open_positions()
        # log.info('current-positions', extra={'positions': [p.dict() for p in open_positions]})

    def report(self, price: PricePoint):
        open_trades = self._api.get_open_trades()
        # report absense of the trades with -1s for all data
        if len(open_trades) > 0:
            trade = open_trades[0]
            trade_summary = self._trade_summary(trade, price)
            self._trainer_api.report_state(
                trade_summary['profit'], trade_summary['pprofit'],
                {'trade_summary': [self._trade_summary(trade, price) for trade in open_trades]}
            )
        else:
            self._trainer_api.report_state(-1, -1, {'state': 'no-open-positions'})

    def open(self, price_point: PricePoint, order_type: OrderType, sl_size=None):
        open_transaction = self._api.open(order_type)
        self._trainer_api.report_transaction(
            open_transaction.id,
            open_transaction.price,
            open_transaction.units,
            {
                'transaction': open_transaction.dict(),
                'interal': {
                    'price': price_point.to_json_ready(),
                    'sl': sl_size,
                    'order_type': order_type.value

                }
            }
        )
        return super().open(price_point, order_type, sl_size)

    def close(self, price_point: PricePoint) -> TradeResult:
        close_transaction = self._api.close_all_trades()
        interal_trade = super().close(price_point)
        if close_transaction is not None:
            self._trainer_api.report_transaction(
                close_transaction.id,
                close_transaction.price,
                close_transaction.units,
                {
                    'transaction': close_transaction.dict(),
                    'interal': {
                        'price': price_point.to_json_ready(),
                        'trade': interal_trade.dict()

                    }
                }
            )
        return interal_trade
