"""Runnable sample script. Demonstrates realtime ticks request with further updates.
"""

import logging
import threading
from datetime import datetime, timedelta, timezone

from tabulate import tabulate
from win32com.client import Dispatch, constants

import CELEnvironment

# region Settings

# Replace it with enabled symbol
SYMBOL = "CLE"

# Logging settings: specify log level and log format here.
# Set log level to DEBUG to see more internal messages.
# See more information about logging lib usage: https://docs.python.org/3/howto/logging-cookbook.html
CELEnvironment.LOG_LEVEL = logging.INFO
# CELEnvironment.LOG_FORMAT = ...

# CELEnvironment.SinkInternal uses own LOG_LEVEL (which is inherited from the global one) and own LOG_FORMAT.
# You can redefine them as follows:
# CELEnvironment.SinkInternal.LOG_LEVEL = logging.DEBUG
# CELEnvironment.SinkInternal.LOG_FORMAT = ...

LOG = CELEnvironment.GetLogger()

# endregion


class TFlowBarsRequestSample(CELEnvironment.CELSinkBase):
    def __init__(self):
        self.eventDone = threading.Event()
        self.celEnvironment = None
        self.bars = None

    def Init(self, celEnvironment):
        self.celEnvironment = celEnvironment

    def Start(self):
        request = Dispatch(self.celEnvironment.cqgCEL.CreateTFlowBarsRequest())
        request.Symbol = SYMBOL
        request.SessionsFilter = 31
        request.UseAggressorFlag = True
        request.RangeStart = datetime.now(timezone.utc) - timedelta(days=1)
        request.RangeEnd = request.RangeStart + timedelta(minutes=15)

        LOG.info("Requesting TFlow bars for time range: %s - %s", request.RangeStart, request.RangeEnd)
        self.bars = self.celEnvironment.cqgCEL.RequestTFlowBars(request)
        self.eventDone.wait(20)

        LOG.info("Done!")

    def OnDataError(self, cqgError, errorDescription):
        if cqgError is not None:
            error = Dispatch(cqgError)
            LOG.error("OnDataError: Code: %d. Description: '%s'", error.Code, error.Description)

        self.eventDone.set()

    def OnTFlowBarsResolved(self, cqgTFlowBars, cqgError):
        if cqgError is not None:
            error = Dispatch(cqgError)
            LOG.error("OnTFlowBarsResolved: Code: %d. Description: '%s'", error.Code, error.Description)
            self.eventDone.set()
            return

        tFlowBars = Dispatch(cqgTFlowBars)
        LOG.info("OnTFlowBarsResolved: Bars count: %d. Bars:\n%s", tFlowBars.Count, self.formatBars(tFlowBars))

    def OnTFlowBarsAdded(self, cqgTFlowBars):
        tFlowBars = Dispatch(cqgTFlowBars)
        LOG.info("OnTFlowBarsAdded: Total bars count: %d. Bars:\n%s", tFlowBars.Count, self.formatBars(tFlowBars))

    def OnTFlowBarsInserted(self, cqgTFlowBars, index):
        tFlowBars = Dispatch(cqgTFlowBars)
        LOG.info(
            "OnTFlowBarsInserted: Inserted bar index: %d. Total bars count: %d. Bars:\n%s",
            index,
            tFlowBars.Count,
            self.formatBars(tFlowBars),
        )

    def OnTFlowBarsRemoved(self, cqgTFlowBars, index):
        tFlowBars = Dispatch(cqgTFlowBars)
        LOG.info(
            "OnTFlowBarsRemoved: Removed bar index: %d. Total bars count: %d. Bars:\n%s",
            index,
            tFlowBars.Count,
            self.formatBars(tFlowBars),
        )

    def OnTFlowBarsUpdated(self, cqgTFlowBars, index):
        tFlowBars = Dispatch(cqgTFlowBars)
        LOG.info(
            "OnTFlowBarsUpdated: Updated bar index: %d. Total bars count: %d. Bars:\n%s",
            index,
            tFlowBars.Count,
            self.formatBars(tFlowBars),
        )

    # region Utility methods

    def validateValue(self, value):
        """Returns value if it is valid, otherwise "N/A" string.
        WARN: should be called only from callbacks.

        Args:
            value (datetime | float | int): Value to check.
        """
        return value if self.celEnvironment.cqgCELForCallback.IsValid(value) else "N/A"

    def getBarRow(self, tFlowBar, index: int) -> list:
        """Returns CQGTFlowBar values as list.
        WARN: should be called only from callbacks.

        Args:
            tFlowBar (CQGTFlowBar): Dispatched CQGTFlowBar object.
            index (int): Bar index.
        """
        res = [index, tFlowBar.High, tFlowBar.Low, tFlowBar.AskVolume, tFlowBar.BidVolume]
        return [self.validateValue(value) for value in res]

    def formatBars(self, bars) -> str:
        """Formats ticks into string as table.
        WARN: should be called only from callbacks.

        Args:
            bars (CQGTFlowBars): Dispatched CQG TFlow bars collection.

        Returns:
            str: Formatted string.
        """
        return tabulate(
            [self.getBarRow(bar, index) for bar, index in zip(bars, range(bars.Count))],
            headers=["Index", "High", "Low", "AskVol", "BidVol"],
        )

    # endregion


def CustomAPIConfiguration(APIConfiguration):
    """If CQGCEL.APIConfiguration must be customized then do it in this function"""
    LOG.info("Set UTC time zone. All time values passed to (received from) CQG API should correspond to this setting. ")
    APIConfiguration.TimeZoneCode = constants.tzUTC


if __name__ == "__main__":
    CELEnvironment.Start(TFlowBarsRequestSample, CustomAPIConfiguration)
