import abc
import datetime
import typing

import QuantConnect
import QuantConnect.Data.UniverseSelection
import QuantConnect.Interfaces
import QuantConnect.Lean.Engine.RealTime
import QuantConnect.Lean.Engine.Results
import QuantConnect.Packets
import QuantConnect.Scheduling
import QuantConnect.Securities
import System
import System.Collections.Concurrent
import System.Collections.Generic


class ScheduledEventFactory(System.Object):
    """Provides methods for creating common scheduled events"""

    @staticmethod
    def EveryDayAt(name: str, dates: System.Collections.Generic.IEnumerable[datetime.datetime], timeOfDay: datetime.timedelta, callback: typing.Callable[[str, datetime.datetime], None], currentUtcTime: typing.Optional[datetime.datetime] = None) -> QuantConnect.Scheduling.ScheduledEvent:
        """
        Creates a new ScheduledEvent that will fire at the specified  for every day in
        
        :param name: An identifier for this event
        :param dates: The dates to set events for at the specified time. These act as a base time to which the  is added to, that is, the implementation does not use .Date before the addition
        :param timeOfDay: The time each tradeable date to fire the event
        :param callback: The delegate to call when an event fires
        :param currentUtcTime: Specfies the current time in UTC, before which, no events will be scheduled. Specify null to skip this filter.
        :returns: A new ScheduledEvent instance that fires events each tradeable day from the start to the finish at the specified time.
        """
        ...

    @staticmethod
    def EveryAlgorithmEndOfDay(algorithm: QuantConnect.Interfaces.IAlgorithm, resultHandler: QuantConnect.Lean.Engine.Results.IResultHandler, start: datetime.datetime, end: datetime.datetime, endOfDayDelta: datetime.timedelta, currentUtcTime: typing.Optional[datetime.datetime] = None) -> QuantConnect.Scheduling.ScheduledEvent:
        """
        Creates a new ScheduledEvent that will fire before market close by the specified time
        
        :param algorithm: The algorithm instance the event is fo
        :param resultHandler: The result handler, used to communicate run time errors
        :param start: The date to start the events
        :param end: The date to end the events
        :param endOfDayDelta: The time difference between the market close and the event, positive time will fire before market close
        :param currentUtcTime: Specfies the current time in UTC, before which, no events will be scheduled. Specify null to skip this filter.
        :returns: The new ScheduledEvent that will fire near market close each tradeable dat.
        """
        ...

    @staticmethod
    def EverySecurityEndOfDay(algorithm: QuantConnect.Interfaces.IAlgorithm, resultHandler: QuantConnect.Lean.Engine.Results.IResultHandler, security: QuantConnect.Securities.Security, start: datetime.datetime, end: datetime.datetime, endOfDayDelta: datetime.timedelta, currentUtcTime: typing.Optional[datetime.datetime] = None) -> QuantConnect.Scheduling.ScheduledEvent:
        """
        Creates a new ScheduledEvent that will fire before market close by the specified time
        
        :param algorithm: The algorithm instance the event is fo
        :param resultHandler: The result handler, used to communicate run time errors
        :param security: The security used for defining tradeable dates
        :param start: The first date for the events
        :param end: The date to end the events
        :param endOfDayDelta: The time difference between the market close and the event, positive time will fire before market close
        :param currentUtcTime: Specfies the current time in UTC, before which, no events will be scheduled. Specify null to skip this filter.
        :returns: The new ScheduledEvent that will fire near market close each tradeable dat.
        """
        ...

    @staticmethod
    def CreateEventName(scope: str, name: str) -> str:
        """
        Defines the format of event names generated by this system.
        
        :param scope: The scope of the event, example, 'Algorithm' or 'Security'
        :param name: A name for this specified event in this scope, example, 'EndOfDay'
        :returns: A string representing a fully scoped event name.
        """
        ...


class BaseRealTimeHandler(System.Object, QuantConnect.Scheduling.IEventSchedule, metaclass=abc.ABCMeta):
    """
    Base class for the real time handler LiveTradingRealTimeHandler
    and BacktestingRealTimeHandler implementations
    """

    @property
    def ScheduledEvents(self) -> System.Collections.Concurrent.ConcurrentDictionary[QuantConnect.Scheduling.ScheduledEvent, int]:
        """
        The scheduled events container
        
        This field is protected.
        """
        ...

    @property
    def Algorithm(self) -> QuantConnect.Interfaces.IAlgorithm:
        """
        The algorithm instance
        
        This field is protected.
        """
        ...

    @Algorithm.setter
    def Algorithm(self, value: QuantConnect.Interfaces.IAlgorithm):
        """
        The algorithm instance
        
        This field is protected.
        """
        ...

    @property
    def ResultHandler(self) -> QuantConnect.Lean.Engine.Results.IResultHandler:
        """
        The result handler instance
        
        This field is protected.
        """
        ...

    @ResultHandler.setter
    def ResultHandler(self, value: QuantConnect.Lean.Engine.Results.IResultHandler):
        """
        The result handler instance
        
        This field is protected.
        """
        ...

    def Add(self, scheduledEvent: QuantConnect.Scheduling.ScheduledEvent) -> None:
        """
        Adds the specified event to the schedule
        
        :param scheduledEvent: The event to be scheduled, including the date/times the event fires and the callback
        """
        ...

    def Remove(self, scheduledEvent: QuantConnect.Scheduling.ScheduledEvent) -> None:
        """
        Removes the specified event from the schedule
        
        :param scheduledEvent: The event to be removed
        """
        ...

    def Setup(self, start: datetime.datetime, end: datetime.datetime, language: QuantConnect.Language, currentUtcTime: typing.Optional[datetime.datetime] = None) -> None:
        """
        Initializes the real time handler for the specified algorithm and job.
        Adds EndOfDayEvents
        
        This method is protected.
        """
        ...

    def GetScheduledEventUniqueId(self) -> int:
        """
        Gets a new scheduled event unique id
        
        This method is protected.
        """
        ...

    def AddAlgorithmEndOfDayEvent(self, start: datetime.datetime, end: datetime.datetime, currentUtcTime: typing.Optional[datetime.datetime] = None) -> None:
        """
        Creates a new ScheduledEvent that will fire before market close by the specified time
        
        This method is protected.
        
        :param start: The date to start the events
        :param end: The date to end the events
        :param currentUtcTime: Specifies the current time in UTC, before which, no events will be scheduled. Specify null to skip this filter.
        """
        ...

    def AddSecurityDependentEndOfDayEvents(self, securities: System.Collections.Generic.IEnumerable[QuantConnect.Securities.Security], start: datetime.datetime, end: datetime.datetime, currentUtcTime: typing.Optional[datetime.datetime] = None) -> None:
        """
        Creates a new ScheduledEvent that will fire before market
        close by the specified time for each provided securities.
        
        This method is protected.
        
        :param securities: The securities for which we want to add the OnEndOfDay event
        :param start: The date to start the events
        :param end: The date to end the events
        :param currentUtcTime: Specifies the current time in UTC, before which, no events will be scheduled. Specify null to skip this filter.
        """
        ...

    def OnSecuritiesChanged(self, changes: QuantConnect.Data.UniverseSelection.SecurityChanges) -> None:
        """Event fired each time that we add/remove securities from the data feed"""
        ...


class IRealTimeHandler(QuantConnect.Scheduling.IEventSchedule, metaclass=abc.ABCMeta):
    """Real time event handler, trigger functions at regular or pretimed intervals"""

    @property
    @abc.abstractmethod
    def IsActive(self) -> bool:
        """Thread status flag."""
        ...

    def Setup(self, algorithm: QuantConnect.Interfaces.IAlgorithm, job: QuantConnect.Packets.AlgorithmNodePacket, resultHandler: QuantConnect.Lean.Engine.Results.IResultHandler, api: QuantConnect.Interfaces.IApi, isolatorLimitProvider: QuantConnect.IIsolatorLimitResultProvider) -> None:
        """Initializes the real time handler for the specified algorithm and job"""
        ...

    def SetTime(self, time: datetime.datetime) -> None:
        """
        Set the current time for the event scanner (so we can use same code for backtesting and live events)
        
        :param time: Current real or backtest time.
        """
        ...

    def ScanPastEvents(self, time: datetime.datetime) -> None:
        """
        Scan for past events that didn't fire because there was no data at the scheduled time.
        
        :param time: Current time.
        """
        ...

    def Exit(self) -> None:
        """Trigger and exit signal to terminate real time event scanner."""
        ...

    def OnSecuritiesChanged(self, changes: QuantConnect.Data.UniverseSelection.SecurityChanges) -> None:
        """Event fired each time that we add/remove securities from the data feed"""
        ...


class BacktestingRealTimeHandler(QuantConnect.Lean.Engine.RealTime.BaseRealTimeHandler, QuantConnect.Lean.Engine.RealTime.IRealTimeHandler):
    """Pseudo realtime event processing for backtesting to simulate realtime events in fast forward."""

    @property
    def IsActive(self) -> bool:
        """
        Flag indicating the hander thread is completely finished and ready to dispose.
        this doesn't run as its own thread
        """
        ...

    def Setup(self, algorithm: QuantConnect.Interfaces.IAlgorithm, job: QuantConnect.Packets.AlgorithmNodePacket, resultHandler: QuantConnect.Lean.Engine.Results.IResultHandler, api: QuantConnect.Interfaces.IApi, isolatorLimitProvider: QuantConnect.IIsolatorLimitResultProvider) -> None:
        """Initializes the real time handler for the specified algorithm and job"""
        ...

    def Add(self, scheduledEvent: QuantConnect.Scheduling.ScheduledEvent) -> None:
        """
        Adds the specified event to the schedule
        
        :param scheduledEvent: The event to be scheduled, including the date/times the event fires and the callback
        """
        ...

    def Remove(self, scheduledEvent: QuantConnect.Scheduling.ScheduledEvent) -> None:
        """
        Removes the specified event from the schedule
        
        :param scheduledEvent: The event to be removed
        """
        ...

    def SetTime(self, time: datetime.datetime) -> None:
        """
        Set the time for the realtime event handler.
        
        :param time: Current time.
        """
        ...

    def ScanPastEvents(self, time: datetime.datetime) -> None:
        """
        Scan for past events that didn't fire because there was no data at the scheduled time.
        
        :param time: Current time.
        """
        ...

    def Exit(self) -> None:
        """Stop the real time thread"""
        ...

    @staticmethod
    def SortFirstElement(scheduledEvents: System.Collections.Generic.IList[QuantConnect.Scheduling.ScheduledEvent]) -> None:
        """
        Sorts the first element of the provided list and supposes the rest of the collection is sorted.
        Supposes the collection has at least 1 element
        """
        ...


class LiveTradingRealTimeHandler(QuantConnect.Lean.Engine.RealTime.BaseRealTimeHandler, QuantConnect.Lean.Engine.RealTime.IRealTimeHandler):
    """Live trading realtime event processing."""

    @property
    def IsActive(self) -> bool:
        """Boolean flag indicating thread state."""
        ...

    @IsActive.setter
    def IsActive(self, value: bool):
        """Boolean flag indicating thread state."""
        ...

    def Setup(self, algorithm: QuantConnect.Interfaces.IAlgorithm, job: QuantConnect.Packets.AlgorithmNodePacket, resultHandler: QuantConnect.Lean.Engine.Results.IResultHandler, api: QuantConnect.Interfaces.IApi, isolatorLimitProvider: QuantConnect.IIsolatorLimitResultProvider) -> None:
        """Initializes the real time handler for the specified algorithm and job"""
        ...

    def Add(self, scheduledEvent: QuantConnect.Scheduling.ScheduledEvent) -> None:
        """
        Adds the specified event to the schedule
        
        :param scheduledEvent: The event to be scheduled, including the date/times the event fires and the callback
        """
        ...

    def Remove(self, scheduledEvent: QuantConnect.Scheduling.ScheduledEvent) -> None:
        """
        Removes the specified event from the schedule
        
        :param scheduledEvent: The event to be removed
        """
        ...

    def SetTime(self, time: datetime.datetime) -> None:
        """Set the current time. If the date changes re-start the realtime event setup routines."""
        ...

    def ScanPastEvents(self, time: datetime.datetime) -> None:
        """
        Scan for past events that didn't fire because there was no data at the scheduled time.
        
        :param time: Current time.
        """
        ...

    def Exit(self) -> None:
        """Stop the real time thread"""
        ...


