import { v4 as uuid } from 'uuid';
import { METHOD_START_MESSAGE } from '../../common/constants';
import { CallDirection, RegistrationStatus, ServiceIndicator, } from '../../common/types';
import { LINE_EVENTS } from './types';
import { LINE_FILE, METHODS, VALID_PHONE_REGEX } from '../constants';
import log from '../../Logger';
import { createRegistration } from '../registration';
import { Eventing } from '../../Events/impl';
import { LineError } from '../../Errors/catalog/LineError';
import { validateServiceData } from '../../common';
import SDKConnector from '../../SDKConnector';
import { LINE_EVENT_KEYS } from '../../Events/types';
import { getCallManager } from '../calling/callManager';
import { ERROR_TYPE } from '../../Errors/types';
export default class Line extends Eventing {
    #webex;
    #mutex;
    #sdkConnector;
    registration;
    userId;
    clientDeviceUri;
    lineId;
    mobiusDeviceId;
    mobiusUri;
    phoneNumber;
    extension;
    sipAddresses = [];
    voicemail;
    lastSeen;
    keepaliveInterval;
    callKeepaliveInterval;
    rehomingIntervalMin;
    rehomingIntervalMax;
    voicePortalNumber;
    voicePortalExtension;
    callManager;
    serviceData;
    #primaryMobiusUris;
    #backupMobiusUris;
    constructor(userId, clientDeviceUri, mutex, primaryMobiusUris, backupMobiusUris, logLevel, serviceDataConfig, jwe, phoneNumber, extension, voicemail) {
        super();
        this.lineId = uuid();
        this.userId = userId;
        this.clientDeviceUri = clientDeviceUri;
        this.phoneNumber = phoneNumber;
        this.extension = extension;
        this.voicemail = voicemail;
        this.#sdkConnector = SDKConnector;
        this.#webex = this.#sdkConnector.getWebex();
        this.#mutex = mutex;
        this.#primaryMobiusUris = primaryMobiusUris;
        this.#backupMobiusUris = backupMobiusUris;
        this.serviceData = serviceDataConfig?.indicator
            ? serviceDataConfig
            : { indicator: ServiceIndicator.CALLING, domain: '' };
        validateServiceData(this.serviceData);
        this.registration = createRegistration(this.#webex, this.serviceData, this.#mutex, this.lineEmitter, logLevel, jwe);
        log.setLogger(logLevel, LINE_FILE);
        this.callManager = getCallManager(this.#webex, this.serviceData.indicator);
        this.incomingCallListener();
    }
    async register() {
        log.info(METHOD_START_MESSAGE, {
            file: LINE_FILE,
            method: METHODS.REGISTER,
        });
        await this.#mutex.runExclusive(async () => {
            this.emit(LINE_EVENTS.CONNECTING);
            this.registration.setMobiusServers(this.#primaryMobiusUris, this.#backupMobiusUris);
            await this.registration.triggerRegistration();
        });
    }
    async deregister(closeMobiusWss = false) {
        log.info(METHOD_START_MESSAGE, {
            file: LINE_FILE,
            method: METHODS.DEREGISTER,
        });
        await this.registration.deregister(closeMobiusWss);
        this.registration.setStatus(RegistrationStatus.IDLE);
    }
    normalizeLine(deviceInfo) {
        const { device, keepaliveInterval, callKeepaliveInterval, rehomingIntervalMin, rehomingIntervalMax, voicePortalNumber, voicePortalExtension, } = deviceInfo;
        this.mobiusDeviceId = device?.deviceId;
        this.mobiusUri = device?.uri;
        this.lastSeen = device?.lastSeen;
        this.sipAddresses = device?.addresses ?? [];
        this.keepaliveInterval = keepaliveInterval;
        this.callKeepaliveInterval = callKeepaliveInterval;
        this.rehomingIntervalMin = rehomingIntervalMin;
        this.rehomingIntervalMax = rehomingIntervalMax;
        this.voicePortalNumber = voicePortalNumber;
        this.voicePortalExtension = voicePortalExtension;
        if (this.mobiusDeviceId) {
            this.callManager.updateLine(this.mobiusDeviceId, this);
        }
    }
    lineEmitter = (event, deviceInfo, lineError) => {
        log.info(METHOD_START_MESSAGE, {
            file: LINE_FILE,
            method: METHODS.LINE_EMITTER,
        });
        switch (event) {
            case LINE_EVENTS.REGISTERED:
                if (deviceInfo) {
                    this.normalizeLine(deviceInfo);
                    this.emit(event, this);
                }
                break;
            case LINE_EVENTS.UNREGISTERED:
            case LINE_EVENTS.RECONNECTED:
            case LINE_EVENTS.RECONNECTING:
                this.emit(event);
                break;
            case LINE_EVENTS.ERROR:
                if (lineError) {
                    this.emit(event, lineError);
                }
                break;
            default:
                break;
        }
    };
    getLoggingLevel() {
        return log.getLogLevel();
    }
    getActiveMobiusUrl() {
        return this.registration.getActiveMobiusUrl();
    }
    getStatus = () => this.registration.getStatus();
    getDeviceId = () => this.registration.getDeviceInfo().device?.deviceId;
    makeCall = (dest) => {
        log.info(METHOD_START_MESSAGE, {
            file: LINE_FILE,
            method: METHODS.MAKE_CALL,
        });
        let call;
        if (dest) {
            const match = dest.address.match(VALID_PHONE_REGEX);
            if (match && match[0].length === dest.address.length) {
                const sanitizedNumber = dest.address
                    .replace(/[^[*+]\d#]/gi, '')
                    .replace(/\s+/gi, '')
                    .replace(/-/gi, '');
                const formattedDest = {
                    type: dest.type,
                    address: `tel:${sanitizedNumber}`,
                };
                call = this.callManager.createCall(CallDirection.OUTBOUND, this.registration.getDeviceInfo().device?.deviceId, this.lineId, formattedDest);
                log.log(`New call created, callId: ${call?.getCallId()}`, {
                    file: LINE_FILE,
                    method: METHODS.MAKE_CALL,
                });
            }
            else {
                log.warn('Invalid phone number detected', {
                    file: LINE_FILE,
                    method: METHODS.MAKE_CALL,
                });
                const err = new LineError('An invalid phone number was detected. Check the number and try again.', {}, ERROR_TYPE.CALL_ERROR, RegistrationStatus.ACTIVE);
                this.emit(LINE_EVENTS.ERROR, err);
            }
            return call;
        }
        if (this.serviceData.indicator === ServiceIndicator.GUEST_CALLING) {
            call = this.callManager.createCall(CallDirection.OUTBOUND, this.registration.getDeviceInfo().device?.deviceId, this.lineId);
            log.info(`New guest call created, callId: ${call.getCallId()}`, {
                file: LINE_FILE,
                method: METHODS.MAKE_CALL,
            });
            return call;
        }
        return undefined;
    };
    incomingCallListener() {
        log.info(METHOD_START_MESSAGE, {
            file: LINE_FILE,
            method: METHODS.INCOMING_CALL_LISTENER,
        });
        log.info('Listening for incoming calls... ', {
            file: LINE_FILE,
            method: METHODS.INCOMING_CALL_LISTENER,
        });
        this.callManager.on(LINE_EVENT_KEYS.INCOMING_CALL, (callObj) => {
            this.emit(LINE_EVENTS.INCOMING_CALL, callObj);
        });
    }
    getCall = (correlationId) => {
        return this.callManager.getCall(correlationId);
    };
}
