import { HubConnection, IStreamResult } from "@microsoft/signalr"

export class TestHub {
    constructor(private connection: HubConnection) {
    }

    getServerTime(): Promise<Date> {
        return this.connection.invoke('GetServerTime');
    }

    ping(message: string): Promise<Void> {
        return this.connection.invoke('Ping', message);
    }

    registerCallbacks(implementation: ITestHubCallbacks) {
        this.connection.on('Pong', (message) => implementation.pong(message));
        this.connection.on('OnClock', (dt) => implementation.onClock(dt));
    }

    unregisterCallbacks(implementation: ITestHubCallbacks) {
        this.connection.off('Pong', (message) => implementation.pong(message));
        this.connection.off('OnClock', (dt) => implementation.onClock(dt));
    }
}

export interface ITestHubCallbacks {
    pong(message: string): void;
    onClock(dt: Date): void;
}

export class MT4Hub {
    constructor(private connection: HubConnection) {
    }

    connect(args: ConnectArgs): Promise<boolean> {
        return this.connection.invoke('Connect', args);
    }

    subscribeToTicks(args: SubscribeToTicksArgs): Promise<void> {
        return this.connection.invoke('SubscribeToTicks', args);
    }

    subscribeToAllTicks(args: TradePlatformId): Promise<void> {
        return this.connection.invoke('SubscribeToAllTicks', args);
    }

    unsubscribeFromAllTicks(args: TradePlatformId): Promise<void> {
        return this.connection.invoke('UnsubscribeFromAllTicks', args);
    }

    unsubscribeFromTicks(args: SubscribeToTicksArgs): Promise<void> {
        return this.connection.invoke('UnsubscribeFromTicks', args);
    }

    subscribeToSymbols(args: TradePlatformId): Promise<void> {
        return this.connection.invoke('SubscribeToSymbols', args);
    }

    unsubscribeFromSymbols(args: TradePlatformId): Promise<void> {
        return this.connection.invoke('UnsubscribeFromSymbols', args);
    }

    subscribeToOnlineUpdates(args: TradePlatformId): Promise<void> {
        return this.connection.invoke('SubscribeToOnlineUpdates', args);
    }

    unsubscribeFromOnlineUpdates(args: TradePlatformId): Promise<void> {
        return this.connection.invoke('UnsubscribeFromOnlineUpdates', args);
    }

    streamTrades(args: TradePlatformId): IStreamResult<TradeExEventPayload> {
        return this.connection.stream('StreamTrades', args);
    }

    streamAllTicks(args: TradePlatformId): IStreamResult<SymbolInfo> {
        return this.connection.stream('StreamAllTicks', args);
    }

    streamMarginCallUpdates(args: TradePlatformId): IStreamResult<MarginLevel> {
        return this.connection.stream('StreamMarginCallUpdates', args);
    }

    streamUserUpdates(args: TradePlatformId): IStreamResult<UserUpdate> {
        return this.connection.stream('StreamUserUpdates', args);
    }

    registerCallbacks(implementation: IMT4HubCallbacks) {
        this.connection.on('OnMT4ConnectionStatus', (data) => implementation.onMT4ConnectionStatus(data));
        this.connection.on('OnTick', (data) => implementation.onTick(data));
        this.connection.on('OnTicks', (data) => implementation.onTicks(data));
        this.connection.on('OnTicksOHLC', (data) => implementation.onTicksOHLC(data));
        this.connection.on('OnMarginUpdate', (data) => implementation.onMarginUpdate(data));
        this.connection.on('OnSymbolUpdate', (data) => implementation.onSymbolUpdate(data));
        this.connection.on('OnOnlineUpdate', (data) => implementation.onOnlineUpdate(data));
        this.connection.on('OnDebug', (data) => implementation.onDebug(data));
    }

    unregisterCallbacks(implementation: IMT4HubCallbacks) {
        this.connection.off('OnMT4ConnectionStatus', (data) => implementation.onMT4ConnectionStatus(data));
        this.connection.off('OnTick', (data) => implementation.onTick(data));
        this.connection.off('OnTicks', (data) => implementation.onTicks(data));
        this.connection.off('OnTicksOHLC', (data) => implementation.onTicksOHLC(data));
        this.connection.off('OnMarginUpdate', (data) => implementation.onMarginUpdate(data));
        this.connection.off('OnSymbolUpdate', (data) => implementation.onSymbolUpdate(data));
        this.connection.off('OnOnlineUpdate', (data) => implementation.onOnlineUpdate(data));
        this.connection.off('OnDebug', (data) => implementation.onDebug(data));
    }
}

export interface IMT4HubCallbacks {
    onMT4ConnectionStatus(data: MT4ConnectionStatusArgs): void;
    onTick(data: TickArgs): void;
    onTicks(data: TicksArgs): void;
    onTicksOHLC(data: TicksOHLCArgs): void;
    onMarginUpdate(data: MarginUpdateArgs): void;
    onSymbolUpdate(data: SymbolUpdateArgs): void;
    onOnlineUpdate(data: OnlineUpdateArgs): void;
    onDebug(data: DebugArgs): void;
}

export interface Void {
}

export interface ConnectArgs {
    tradePlatform: string;
    /** Timeout of pumping connection being UP and running. In milliseconds;
default = 30 seconds; */
    timeoutMs: number;
}

export interface SubscribeToTicksArgs {
    tradePlatform: string;
    symbol: string;
}

export interface TradePlatformId {
    tradePlatform: string;
}

export interface TradeExEventPayload extends TradeRecordDTO {
    /** add - order opened, delete - order closed, Update - IF login==0 THEN order deleted ELSE order updated */
    updateType: TransactionType;
}

export enum TransactionType {
    Add = "Add",
    Delete = "Delete",
    Update = "Update",
    ChangeGrp = "ChangeGrp",
}

export interface TradeRecordDTO {
    /** order ticket */
    order: number;
    /** owner's login */
    login: number;
    /** security precision */
    digits: number;
    /** it stores 100 times more volue than it is, for example value of 15 means 10/100=0.15 lots
             */
    volume: number;
    /** reserved */
    openPrice: number;
    sl: number;
    tp: number;
    gatewayVolume: number;
    commission: number;
    /** agent commission */
    commissionAgent: number;
    /** order swaps */
    storage: number;
    closePrice: number;
    profit: number;
    taxes: number;
    /** special value used by client experts */
    magic: number;
    /** trade order ticket on master server in STP */
    gatewayOrder: number;
    /** gateway order price deviation (pips) from order open price */
    gatewayOpenPrice: number;
    /** gateway order price deviation (pips) from order close price */
    gatewayClosePrice: number;
    /** margin convertation rate (rate of convertation from margin currency to deposit one)
             */
    marginRate: number;
    /** security */
    symbol: string;
    /** trade command (buy, sell, pending, balance, etc) */
    tradeCommand: TradeCommand;
    tradeRecordState: TradeRecordState;
    /** Volume in lots, for convenience */
    gatewayVolumeLots: number;
    tradeRecordReason: TradeRecordReason;
    /**   */
    convReserv: string;
    /** conversation rates from profit currency to group deposit currency
(first element-for open time, second element-for close time)
             */
    convRates: number[];
    comment: string;
    activationType: ActivationType;
    timeStamp: Date;
    /**   */
    apiData: number[];
    openTime: Date;
    closeTime: Date;
    /** pending order's expiration time */
    expiration: Date;
    /** Volume in lots, for convenience */
    volumeLots: number;
    /** add - order opened, delete - order closed, Update - IF login==0 THEN order deleted ELSE order updated */
    updateType: TransactionType;
}

export enum TradeCommand {
    Buy = "Buy",
    Sell = "Sell",
    BuyLimit = "BuyLimit",
    SellLimit = "SellLimit",
    BuyStop = "BuyStop",
    SellStop = "SellStop",
    Balance = "Balance",
    Credit = "Credit",
}

export enum TradeRecordState {
    OpenNormal = "OpenNormal",
    OpenRemand = "OpenRemand",
    OpenRestored = "OpenRestored",
    ClosedNormal = "ClosedNormal",
    ClosedPart = "ClosedPart",
    ClosedBy = "ClosedBy",
    Deleted = "Deleted",
}

export enum TradeRecordReason {
    Client = "Client",
    Expert = "Expert",
    Dealer = "Dealer",
    Signal = "Signal",
    Gateway = "Gateway",
    Mobile = "Mobile",
    Web = "Web",
    API = "API",
}

export enum ActivationType {
    None = "None",
    SL = "SL",
    TP = "TP",
    Pending = "Pending",
    Stopout = "Stopout",
    StopOutRollback = "StopOutRollback",
    PendingRollback = "PendingRollback",
    TPRollback = "TPRollback",
    SLRollback = "SLRollback",
}

export interface SymbolInfo {
    lastTime: Date | undefined;
    visible: boolean;
    symbol: string | undefined;
    digits: number;
    count: number;
    type: number;
    point: number;
    spread: number;
    spreadBalance: number;
    direction: SymbolPriceDirection;
    updateFlag: number;
    bid: number;
    ask: number;
    high: number;
    low: number;
    commission: number;
    commType: CommissionType;
}

export enum SymbolPriceDirection {
    Up = "Up",
    Down = "Down",
    None = "None",
}

export enum CommissionType {
    Money = "Money",
    Pips = "Pips",
    Percent = "Percent",
}

export interface MarginLevel {
    group: string | undefined;
    login: number;
    leverage: number;
    updated: number;
    balance: number;
    equity: number;
    volume: number;
    margin: number;
    free: number;
    level: number;
    controllingType: MarginControllingType;
    levelType: MarginLevelType;
}

export enum MarginControllingType {
    Percent = "Percent",
    Currency = "Currency",
}

export enum MarginLevelType {
    Ok = "Ok",
    MarginCall = "MarginCall",
    StopOut = "StopOut",
}

export interface UserUpdate {
    type: TransactionType;
    user: UserRecord;
}

export interface UserRecord {
    regDate: Date;
    lastDate: Date;
    timeStamp: Date;
    login: number;
    group: string | undefined;
    password: string | undefined;
    enable: number;
    enableChangePassword: number;
    enableReadOnly: number;
    enableOTP: number;
    enableFlags: number;
    enableReserved: number[] | undefined;
    passwordInvestor: string | undefined;
    passwordPhone: string | undefined;
    name: string | undefined;
    country: string | undefined;
    city: string | undefined;
    state: string | undefined;
    zipCode: string | undefined;
    address: string | undefined;
    leadSource: string | undefined;
    phone: string | undefined;
    email: string | undefined;
    comment: string | undefined;
    id: string | undefined;
    status: string | undefined;
    leverage: number;
    agentAccount: number;
    lastIP: number;
    balance: number;
    prevMonthBalance: number;
    prevBalance: number;
    credit: number;
    interestRate: number;
    taxes: number;
    prevMonthEquity: number;
    prevEquity: number;
    reserved2: number[] | undefined;
    otpSecret: string | undefined;
    secureReserved: string | undefined;
    sendReports: number;
    mqid: number;
    userColor: number;
    unused: string | undefined;
    apiData: string | undefined;
}

export interface MT4ConnectionStatusArgs extends TradePlatformId {
    connected: boolean;
}

export interface TickArgs extends TradePlatformId {
    tick: Tick;
}

export interface Tick {
    symbol: string;
    bid: number;
    ask: number;
    lastTime: Date | undefined;
}

export interface TicksArgs extends TradePlatformId {
    ticks: TickArgs[];
}

export interface TicksOHLCArgs extends TradePlatformId {
    ticks: OHLCS[];
}

export interface OHLCS {
    /** Priced being at the beginning of interval */
    open: BidAsk;
    /** highest ever bid and ask, calculated separately. */
    maximum: BidAsk;
    /** lowest ever bid and ask, calculated separately. */
    minimum: BidAsk;
    /** Priced being at the end of interval */
    close: BidAsk;
    symbol: string;
    lastTime: Date | undefined;
    /** number of ticks before groupping */
    volume: number;
}

export interface BidAsk {
    bid: number;
    ask: number;
}

export interface MarginUpdateArgs extends TradePlatformId {
    margins: MarginLevel[];
}

export interface SymbolUpdateArgs extends TradePlatformId {
    type: TransactionType;
    symbol: ConSymbol;
}

export interface ConSymbol {
    symbol: string | undefined;
    description: string | undefined;
    source: string | undefined;
    currency: string | undefined;
    type: number;
    digits: number;
    tradeMode: TradeMode;
    backgroundColor: number;
    count: number;
    countOriginal: number;
    realtime: number;
    starting: Date;
    expiration: Date;
    sessions: ConSessions[] | undefined;
    profitCalculationMode: ProfitCalculationMode;
    profitReserved: number;
    filter: number;
    filterCounter: number;
    filterLimit: number;
    filterSmoothing: number;
    filterReserved: number;
    logging: number;
    spread: number;
    spreadBalance: number;
    symbolExecMode: SymbolExecMode;
    swapEnable: number;
    swapType: SwapType;
    swapLong: number;
    swapShort: number;
    swapRollover3Days: number;
    contractSize: number;
    tickValue: number;
    tickSize: number;
    stopsLevel: number;
    gtcMode: GTCMode;
    marginCalculationMode: MarginCalculationMode;
    marginInitial: number;
    marginMaintenance: number;
    marginHedged: number;
    marginDivider: number;
    percentage: number;
    point: number;
    multiply: number;
    bidTickValue: number;
    askTickValue: number;
    longOnly: number;
    instantMaxVolume: number;
    marginCurrency: string | undefined;
    freezeLevel: number;
    marginHedgedStrong: number;
    valueDate: Date;
    quotesDelay: number;
    swapOpenPrice: number;
    swapVariationMargin: number;
}

export enum TradeMode {
    No = "No",
    Close = "Close",
    Full = "Full",
}

export interface ConSessions {
    quoteOvernight: number;
    tradeOvernight: number;
    quote: ConSession[] | undefined;
    trade: ConSession[] | undefined;
}

export interface ConSession {
    openHour: number;
    openMin: number;
    closeHour: number;
    closeMin: number;
    align: number[] | undefined;
}

export enum ProfitCalculationMode {
    Forex = "Forex",
    CFD = "CFD",
    Futures = "Futures",
}

export enum SymbolExecMode {
    Request = "Request",
    Instant = "Instant",
    Market = "Market",
}

export enum SwapType {
    Points = "Points",
    Dollars = "Dollars",
    Interest = "Interest",
    MarginCurrency = "MarginCurrency",
}

export enum GTCMode {
    Daily = "Daily",
    GTC = "GTC",
    DailyNoStops = "DailyNoStops",
}

export enum MarginCalculationMode {
    Forex = "Forex",
    CFD = "CFD",
    Futures = "Futures",
    CFDIndex = "CFDIndex",
    CFDLeverage = "CFDLeverage",
}

export interface OnlineUpdateArgs extends TradePlatformId {
    type: TransactionType;
    login: number;
}

export interface DebugArgs extends TradePlatformId {
    category: string;
    message: string;
    data: any;
}