import { APIRequest } from '../utils/request';
import { METHOD_START_MESSAGE } from '../../common/constants';
import { CALL_MANAGER_FILE, METHODS } from '../constants';
import { CALLING_CLIENT_EVENT_KEYS, LINE_EVENT_KEYS } from '../../Events/types';
import { Eventing } from '../../Events/impl';
import SDKConnector from '../../SDKConnector';
import { CallDirection } from '../../common/types';
import { MediaState, MobiusEventType, } from './types';
import { createCall } from './call';
import log from '../../Logger';
let callManager;
export class CallManager extends Eventing {
    sdkConnector;
    webex;
    callCollection;
    activeMobiusUrl;
    serviceIndicator;
    lineDict;
    apiRequest;
    constructor(webex, indicator) {
        super();
        this.sdkConnector = SDKConnector;
        this.serviceIndicator = indicator;
        if (!this.sdkConnector.getWebex()) {
            SDKConnector.setWebex(webex);
        }
        this.lineDict = {};
        this.webex = this.sdkConnector.getWebex();
        this.apiRequest = APIRequest.getInstance({ webex: this.webex });
        this.callCollection = {};
        this.activeMobiusUrl = '';
        this.listenForWsEvents();
    }
    createCall = (direction, deviceId, lineId, destination) => {
        log.info(`${METHOD_START_MESSAGE} with ${direction}, ${deviceId} and ${lineId}`, {
            file: CALL_MANAGER_FILE,
            method: METHODS.CREATE_CALL,
        });
        log.log('Creating call object', {});
        const newCall = createCall(this.activeMobiusUrl, this.webex, direction, deviceId, lineId, (correlationId) => {
            delete this.callCollection[correlationId];
            const activeCalls = Object.keys(this.getActiveCalls()).length;
            log.info(`DELETE:: Deleted corelationId: ${newCall.getCorrelationId()} from CallManager, Number of call records :- ${activeCalls}`, {});
            if (activeCalls === 0) {
                this.emit(CALLING_CLIENT_EVENT_KEYS.ALL_CALLS_CLEARED);
                log.log('All calls have been cleared', {
                    file: CALL_MANAGER_FILE,
                    method: METHODS.CREATE_CALL,
                });
            }
        }, this.serviceIndicator, destination);
        this.callCollection[newCall.getCorrelationId()] = newCall;
        log.log(`New call created with correlationId: ${newCall.getCorrelationId()}`, {});
        log.info(`ADD:: Added corelationId: ${newCall.getCorrelationId()} to CallManager , Number of call records now:- ${Object.keys(this.getActiveCalls()).length}`, {});
        return newCall;
    };
    updateActiveMobius(url) {
        this.activeMobiusUrl = url;
        log.log(`Successfully updated active Mobius URL to: ${url}`, {
            file: CALL_MANAGER_FILE,
            method: METHODS.UPDATE_ACTIVE_MOBIUS,
        });
    }
    listenForWsEvents() {
        if (!this.apiRequest.isSocketEnabled()) {
            this.sdkConnector.registerListener('event:mobius', async (event) => {
                this.dequeueWsEvents(event);
            });
            log.info('Successfully registered listener for Mobius events', {
                file: CALL_MANAGER_FILE,
                method: METHODS.REGISTER_SESSIONS_LISTENER,
            });
        }
    }
    dequeueWsEvents(event) {
        log.info(`${METHOD_START_MESSAGE} with event ${event}`, {
            file: CALL_MANAGER_FILE,
            method: METHODS.DEQUEUE_WS_EVENTS,
        });
        const mobiusEvent = event;
        const { callId, correlationId } = mobiusEvent.data;
        switch (mobiusEvent.data.eventType) {
            case MobiusEventType.CALL_SETUP: {
                log.log(`Received call Setup message for call: ${callId}`, {
                    file: CALL_MANAGER_FILE,
                    method: METHODS.DEQUEUE_WS_EVENTS,
                });
                if (mobiusEvent.data.midCallService) {
                    mobiusEvent.data.midCallService.forEach((midCallEvent) => {
                        const call = this.getCall(correlationId);
                        if (call) {
                            call.handleMidCallEvent(midCallEvent);
                        }
                        else {
                            log.info(`Dropping midcall event of type: ${midCallEvent.eventType} as it doesn't match with any existing call`, {
                                file: CALL_MANAGER_FILE,
                                method: METHODS.DEQUEUE_WS_EVENTS,
                            });
                        }
                    });
                    return;
                }
                const newId = Object.keys(this.callCollection).find((id) => this.callCollection[id].getCallId() === callId);
                let newCall;
                if (!newId) {
                    const lineId = this.getLineId(mobiusEvent.data.deviceId);
                    newCall = this.createCall(CallDirection.INBOUND, mobiusEvent.data.deviceId, lineId, {});
                    log.log(`New incoming call created with correlationId from Call Setup message: ${newCall.getCorrelationId()}`, {
                        file: CALL_MANAGER_FILE,
                        method: METHODS.DEQUEUE_WS_EVENTS,
                    });
                    newCall.setCallId(callId);
                    if (mobiusEvent.data.broadworksCorrelationInfo) {
                        log.info(`Found broadworksCorrelationInfo: ${mobiusEvent.data.broadworksCorrelationInfo}`, {
                            file: CALL_MANAGER_FILE,
                            method: METHODS.DEQUEUE_WS_EVENTS,
                        });
                        newCall.setBroadworksCorrelationInfo(mobiusEvent.data.broadworksCorrelationInfo);
                    }
                }
                else {
                    log.info(`Found the call Object with a matching callId: ${callId} from our records with correlationId: ${newId}`, {
                        file: CALL_MANAGER_FILE,
                        method: METHODS.DEQUEUE_WS_EVENTS,
                    });
                    newCall = this.getCall(newId);
                }
                if (mobiusEvent.data.callerId) {
                    log.info('Processing Caller-Id data', {
                        file: CALL_MANAGER_FILE,
                        method: METHODS.DEQUEUE_WS_EVENTS,
                    });
                    newCall.startCallerIdResolution(mobiusEvent.data.callerId);
                }
                this.emit(LINE_EVENT_KEYS.INCOMING_CALL, newCall);
                newCall.sendCallStateMachineEvt({ type: 'E_RECV_CALL_SETUP', data: mobiusEvent.data });
                break;
            }
            case MobiusEventType.CALL_PROGRESS: {
                log.log(`Received call progress mobiusEvent for call: ${correlationId}`, {
                    file: CALL_MANAGER_FILE,
                    method: METHODS.DEQUEUE_WS_EVENTS,
                });
                const call = this.getCall(correlationId);
                if (mobiusEvent.data.callerId) {
                    log.info('Processing Caller-Id data', {
                        file: CALL_MANAGER_FILE,
                        method: METHODS.DEQUEUE_WS_EVENTS,
                    });
                    call.startCallerIdResolution(mobiusEvent.data.callerId);
                }
                call.sendCallStateMachineEvt({ type: 'E_RECV_CALL_PROGRESS', data: mobiusEvent.data });
                break;
            }
            case MobiusEventType.CALL_INFO: {
                log.log(`Received call info mobiusEvent for call: ${correlationId}`, {
                    file: CALL_MANAGER_FILE,
                    method: METHODS.DEQUEUE_WS_EVENTS,
                });
                const call = this.getCall(correlationId);
                if (call && mobiusEvent.data.callerId) {
                    log.info('Processing Caller-Id data', {
                        file: CALL_MANAGER_FILE,
                        method: METHODS.DEQUEUE_WS_EVENTS,
                    });
                    call.startCallerIdResolution(mobiusEvent.data.callerId);
                }
                break;
            }
            case MobiusEventType.CALL_MEDIA: {
                log.log(`Received call media mobiusEvent for call: ${correlationId}`, {
                    file: CALL_MANAGER_FILE,
                    method: METHODS.DEQUEUE_WS_EVENTS,
                });
                let activeCall;
                if (correlationId) {
                    activeCall = this.getCall(correlationId);
                }
                else {
                    const newId = Object.keys(this.callCollection).find((id) => this.callCollection[id].getCallId() === callId);
                    if (newId) {
                        log.info(`Found the call Object with a matching callId: ${callId} from our records with correlationId: ${newId}`, {
                            file: CALL_MANAGER_FILE,
                            method: METHODS.DEQUEUE_WS_EVENTS,
                        });
                        activeCall = this.getCall(newId);
                    }
                    else {
                        const lineId = this.getLineId(mobiusEvent.data.deviceId);
                        activeCall = this.createCall(CallDirection.INBOUND, mobiusEvent.data.deviceId, lineId, {});
                        log.log(`New incoming call created with correlationId from ROAP Message: ${activeCall.getCorrelationId()}`, {
                            file: CALL_MANAGER_FILE,
                            method: METHODS.DEQUEUE_WS_EVENTS,
                        });
                        activeCall.setCallId(callId);
                    }
                }
                if (activeCall) {
                    log.info(`SDP from mobius ${mobiusEvent.data.message?.sdp}`, {
                        file: CALL_MANAGER_FILE,
                        method: METHODS.DEQUEUE_WS_EVENTS,
                    });
                    log.log(`ROAP message from mobius with type:  ${mobiusEvent.data.message?.messageType}, seq: ${mobiusEvent.data.message?.seq} , version: ${mobiusEvent.data.message?.version}`, {
                        file: CALL_MANAGER_FILE,
                        method: METHODS.DEQUEUE_WS_EVENTS,
                    });
                    const mediaState = mobiusEvent.data.message?.messageType;
                    switch (mediaState) {
                        case MediaState.OFFER: {
                            log.log('Received OFFER', {
                                file: CALL_MANAGER_FILE,
                                method: METHODS.DEQUEUE_WS_EVENTS,
                            });
                            activeCall.sendMediaStateMachineEvt({
                                type: 'E_RECV_ROAP_OFFER',
                                data: mobiusEvent.data.message,
                            });
                            break;
                        }
                        case MediaState.ANSWER: {
                            log.log('Received ANSWER', {
                                file: CALL_MANAGER_FILE,
                                method: METHODS.DEQUEUE_WS_EVENTS,
                            });
                            activeCall.sendMediaStateMachineEvt({
                                type: 'E_RECV_ROAP_ANSWER',
                                data: mobiusEvent.data.message,
                            });
                            break;
                        }
                        case MediaState.OFFER_REQUEST: {
                            log.log('Received OFFER_REQUEST', {
                                file: CALL_MANAGER_FILE,
                                method: METHODS.DEQUEUE_WS_EVENTS,
                            });
                            activeCall.sendMediaStateMachineEvt({
                                type: 'E_RECV_ROAP_OFFER_REQUEST',
                                data: mobiusEvent.data.message,
                            });
                            break;
                        }
                        case MediaState.OK: {
                            log.log('Received OK', {
                                file: CALL_MANAGER_FILE,
                                method: METHODS.DEQUEUE_WS_EVENTS,
                            });
                            const mediaOk = {
                                received: true,
                                message: mobiusEvent.data.message,
                            };
                            activeCall.sendMediaStateMachineEvt({
                                type: 'E_ROAP_OK',
                                data: mediaOk,
                            });
                            break;
                        }
                        case MediaState.ERROR: {
                            log.log('Received Error...', {
                                file: CALL_MANAGER_FILE,
                                method: METHODS.DEQUEUE_WS_EVENTS,
                            });
                            break;
                        }
                        default: {
                            log.log(`Unknown Media mobiusEvent: ${mediaState} `, {
                                file: CALL_MANAGER_FILE,
                                method: METHODS.DEQUEUE_WS_EVENTS,
                            });
                        }
                    }
                }
                else {
                    log.info(`CorrelationId: ${correlationId} doesn't exist , discarding..`, {
                        file: CALL_MANAGER_FILE,
                        method: METHODS.DEQUEUE_WS_EVENTS,
                    });
                }
                break;
            }
            case MobiusEventType.CALL_CONNECTED: {
                log.log(`Received call connect for call: ${correlationId}`, {
                    file: CALL_MANAGER_FILE,
                    method: METHODS.DEQUEUE_WS_EVENTS,
                });
                const call = this.getCall(correlationId);
                call.sendCallStateMachineEvt({ type: 'E_RECV_CALL_CONNECT', data: mobiusEvent.data });
                break;
            }
            case MobiusEventType.CALL_DISCONNECTED: {
                log.log(`Received call disconnect for call: ${correlationId}`, {
                    file: CALL_MANAGER_FILE,
                    method: METHODS.DEQUEUE_WS_EVENTS,
                });
                const call = this.getCall(correlationId);
                if (call) {
                    call.sendCallStateMachineEvt({ type: 'E_RECV_CALL_DISCONNECT' });
                }
                break;
            }
            default: {
                log.log(`Unknown Call Event mobiusEvent: ${mobiusEvent.data.eventType}`, {
                    file: CALL_MANAGER_FILE,
                    method: METHODS.DEQUEUE_WS_EVENTS,
                });
            }
        }
    }
    getCall = (correlationId) => {
        return this.callCollection[correlationId];
    };
    getActiveCalls = () => {
        return this.callCollection;
    };
    updateLine(deviceId, line) {
        this.lineDict[deviceId] = line;
        log.log(`Successfully updated line for deviceId: ${deviceId}`, {
            file: CALL_MANAGER_FILE,
            method: METHODS.UPDATE_LINE,
        });
    }
    getLineId(deviceId) {
        return this.lineDict[deviceId].lineId;
    }
}
export const getCallManager = (webex, indicator) => {
    if (!callManager) {
        callManager = new CallManager(webex, indicator);
    }
    return callManager;
};
