"""The sample demonstrates how to make a request for quotes (RFQ)"""

import logging
import threading
import time

import win32com.client
from win32com.client import constants

import CELEnvironment
from CELEnvironment import AssertMessage

# region Settings

# Replace it with real login before run
LOGIN = "your_gw_login"
PASSWORD = "your_gw_password"

# Replace it with enabled symbol
EXPRESSION = "UDS(C.US.EP?2-C.US.EP?1, CalC)"

# Replace it with a real originator info (optional, set empty strings if not needed)
LOCATION_ID = "US,NY"
OPERATOR_ID = "tst-opt"

# Replace it with the desired GW account name
ACCOUNT_NAME = ""

# Events waiting time
TIMEOUT = 30

# 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 RFQSample(CELEnvironment.CELSinkBase):
    def __init__(self):
        self.account = None
        self.dispatchedRFQ = None
        self.eventGatewayIsUp = threading.Event()
        self.eventGatewayIsDown = threading.Event()
        self.eventAccountIsReady = threading.Event()
        self.eventUDSIsReady = threading.Event()
        self.eventRFQIsReady = threading.Event()

    def Init(self, celEnvironment):
        self.celEnvironment = celEnvironment

    def Start(self):
        LOG.info("Connecting to GW")
        self.celEnvironment.cqgCEL.GWLogon(LOGIN, PASSWORD)

        LOG.info("Waiting for GW connection...")
        AssertMessage(self.eventGatewayIsUp.wait(TIMEOUT), "GW connection timeout!")

        self.celEnvironment.cqgCEL.AccountSubscriptionLevel = constants.aslAccountsOnly
        LOG.info("Waiting for accounts coming...")
        AssertMessage(self.eventAccountIsReady.wait(TIMEOUT), "Accounts coming timeout!")

        accounts = win32com.client.Dispatch(self.celEnvironment.cqgCEL.Accounts)
        # Select an account that has enablement for UDS
        self.account = _getAccount(accounts)

        LOG.info("Define strategy...")
        udsDefinitionRequest = self.celEnvironment.cqgCEL.CreateStrategyDefinitionRequest()
        udsDefinitionRequest.Expression = EXPRESSION
        udsDefinitionRequest.Account = self.account
        udsDefinitionRequest.OriginatorInfo.LocationID = LOCATION_ID
        udsDefinitionRequest.OriginatorInfo.OperatorID = OPERATOR_ID
        self.uds = self.celEnvironment.cqgCEL.InvokeStrategyDefinitionRequest(udsDefinitionRequest)

        LOG.info("UDS waiting...")
        AssertMessage(self.eventUDSIsReady.wait(TIMEOUT), "UDS resolution timeout!")
        time.sleep(1)

        LOG.info("Define RFQ(%s)...", self.uds.RequestString)
        self.dispatchedRFQ = win32com.client.Dispatch(
            self.celEnvironment.cqgCEL.CreateStrategyQuoteRequest(self.account, self.uds.RequestString)
        )
        self.dispatchedRFQ.Side = constants.osdUndefined
        self.dispatchedRFQ.Size = -1
        self.dispatchedRFQ.AlgorithmID = 100
        self.dispatchedRFQ.AlgorithmDescription = "My-Algo"
        self.dispatchedRFQ.Manual = False
        self.dispatchedRFQ.OriginatorInfo.OperatorID = OPERATOR_ID
        self.dispatchedRFQ.OriginatorInfo.LocationID = LOCATION_ID

        LOG.info("Invoke RFQ...")
        self.celEnvironment.cqgCEL.InvokeStrategyQuoteRequest(self.dispatchedRFQ)

        LOG.info("Waiting for RFQ result...")
        AssertMessage(self.eventRFQIsReady.wait(TIMEOUT), "RFQ result waiting timeout!")

        LOG.info("Logoff from GW")
        self.eventGatewayIsDown.clear()
        self.celEnvironment.cqgCEL.GWLogoff()
        AssertMessage(self.eventGatewayIsDown.wait(TIMEOUT), "GW disconnection timeout!")

        LOG.info("Done!")

    def OnGWConnectionStatusChanged(self, newStatus):
        if newStatus == constants.csConnectionUp:
            LOG.info("GW connection is UP!")
            self.eventGatewayIsUp.set()
        if newStatus == constants.csConnectionDown:
            LOG.info("GW connection is DOWN!")
            self.eventGatewayIsDown.set()

    def OnAccountChanged(self, changeType, cqgAccount, cqgPosition):
        if changeType != constants.actAccountsReloaded:
            return

        LOG.info("Accounts are ready!")
        self.eventAccountIsReady.set()

    def OnStrategyDefinitionProgress(self, cqgDefinition, cqgError):
        if cqgError is not None:
            error = win32com.client.Dispatch(cqgError)
            LOG.error("OnStrategyDefinitionProgress error: Code: %d Description: %s", error.Code, error.Description)
            return

        definition = win32com.client.Dispatch(cqgDefinition)
        LOG.info(
            "OnStrategyDefinitionProgress: requested symbol: %s GW symbol: %s status: %d operator: %s",
            definition.RequestString,
            definition.Symbol,
            definition.Status,
            definition.OperatorID,
        )
        if definition.Status == constants.srsSuccess:
            self.eventUDSIsReady.set()

    def OnStrategyQuoteRequestResolved(self, cqgRFQ, isProcessed, cqgError):
        if cqgError is not None:
            error = win32com.client.Dispatch(cqgError)
            LOG.error("OnStrategyQuoteRequestResolved error: Code: %d Description: %s", error.Code, error.Description)
            return

        rfq = win32com.client.Dispatch(cqgRFQ)
        originatorInfo = win32com.client.Dispatch(rfq.OriginatorInfo)
        LOG.info(
            "OnStrategyQuoteRequestResolved: requested symbol: %s isProcessed: %s isManual: %s operator: %s",
            rfq.Symbol,
            isProcessed,
            rfq.Manual,
            originatorInfo.OperatorID,
        )
        self.eventRFQIsReady.set()


def _getAccount(accounts):
    for acc in accounts:
        if acc.GWAccountName == ACCOUNT_NAME:
            return acc

    raise RuntimeError(f"Account('{ACCOUNT_NAME}') not found. All accounts: {[acc.GWAccountName for acc in accounts]}")


if __name__ == "__main__":
    CELEnvironment.Start(RFQSample)
