import * as i0 from '@angular/core';
import { Injectable, InjectionToken, NgModule } from '@angular/core';
import { tryParseJson, isFalsy } from '@angular-ru/cdk/utils';
import { Subject, ReplaySubject, throwError } from 'rxjs';
import { filter, map, catchError, takeUntil, take } from 'rxjs/operators';
import { webSocket } from 'rxjs/webSocket';

class WebsocketConfig {
    constructor() {
        this.reconnectionDelay = 2000;
        this.binaryType = 'arraybuffer';
    }
}
/** @nocollapse */ WebsocketConfig.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.2", ngImport: i0, type: WebsocketConfig, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
/** @nocollapse */ WebsocketConfig.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.3.2", ngImport: i0, type: WebsocketConfig });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.2", ngImport: i0, type: WebsocketConfig, decorators: [{
            type: Injectable
        }] });

const PLAIN_TEXT = {};
const BINARY = {};
class AbstractWebsocketClient {
    constructor(wsConfig, ngZone) {
        this.wsConfig = wsConfig;
        this.ngZone = ngZone;
        this.connected = false;
        this.messages$ = new Subject();
        this.connected$ = new ReplaySubject(Number.POSITIVE_INFINITY);
        this.disconnected$ = new ReplaySubject(Number.POSITIVE_INFINITY);
        this.destroy$ = new Subject();
    }
    get isConnected() {
        return this.connected;
    }
    get isDisconnected() {
        return !this.connected;
    }
    get connectionPath() {
        return `${this.baseUrl}${this.handlerPath}`;
    }
    get webSocketSubjectConfig() {
        return {
            openObserver: { next: (event) => this.onOpenObserver(event) },
            closeObserver: { next: (event) => this.onCloseObserver(event) },
            serializer: (message) => this.serialize(message),
            deserializer: (event) => this.deserialize(event),
            url: this.connectionPath,
            binaryType: this.wsConfig.binaryType
        };
    }
    static isArrayBuffer(value) {
        return value instanceof ArrayBuffer || value instanceof Blob;
    }
    onConnected(_event) {
        // noop
    }
    onDisconnected(_event) {
        // noop
    }
    connect(path) {
        this.handlerPath = path;
        this.ngZone.runOutsideAngular(() => this._connect());
        this.destroy$ = new Subject();
    }
    disconnect() {
        this.socketSubscription?.unsubscribe();
        this.socket$?.complete();
        this.destroy$.next(true);
        this.destroy$.complete();
    }
    on(type) {
        return this.messages$.pipe(filter((message) => message.type === type), map((message) => message.data), catchError((error) => throwError(() => error)), takeUntil(this.destroy$));
    }
    single(type) {
        return this.on(type).pipe(take(1));
    }
    sendMessage(type, data) {
        this.socket$?.next({ type, data });
    }
    ngOnDestroy() {
        this.disconnect();
    }
    serialize(message) {
        switch (message.type) {
            case BINARY:
            case PLAIN_TEXT:
                return message.data;
            default:
                return JSON.stringify(message);
        }
    }
    deserialize({ data }) {
        if (AbstractWebsocketClient.isArrayBuffer(data)) {
            return { type: BINARY, data };
        }
        else {
            return tryParseJson(data) ?? { type: PLAIN_TEXT, data };
        }
    }
    onOpenObserver(event) {
        this.connected = true;
        this.connected$.next(event);
        this.onConnected(event);
    }
    onCloseObserver(event) {
        this.connected = false;
        this.disconnected$.next(event);
        this.onDisconnected(event);
    }
    _connect() {
        this.socket$ = webSocket(this.webSocketSubjectConfig);
        this.socketSubscription = this.socket$.pipe(takeUntil(this.destroy$)).subscribe({
            next: (message) => this.messages$.next(message),
            error: () => this.reconnect()
        });
    }
    reconnect() {
        this.completeStoppedSocket();
        this.socketUnsubscribe();
        this.ngZone.runOutsideAngular(() => {
            // eslint-disable-next-line no-restricted-properties
            window.setTimeout(() => this._connect(), this.wsConfig.reconnectionDelay);
        });
    }
    completeStoppedSocket() {
        // TODO: need replace isStopped
        // eslint-disable-next-line deprecation/deprecation
        if (isFalsy(this.socket$?.isStopped)) {
            this.socket$?.complete();
        }
    }
    socketUnsubscribe() {
        if (isFalsy(this.socketSubscription?.closed)) {
            this.socketSubscription?.unsubscribe();
        }
    }
}
/** @nocollapse */ AbstractWebsocketClient.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.2", ngImport: i0, type: AbstractWebsocketClient, deps: [{ token: WebsocketConfig }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Injectable });
/** @nocollapse */ AbstractWebsocketClient.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.3.2", ngImport: i0, type: AbstractWebsocketClient });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.2", ngImport: i0, type: AbstractWebsocketClient, decorators: [{
            type: Injectable
        }], ctorParameters: function () { return [{ type: WebsocketConfig }, { type: i0.NgZone }]; } });

const WEBSOCKET_CONFIGS = new InjectionToken('WEBSOCKET_CONFIGS');

class WebsocketModule {
    static forRoot(config = {}) {
        return {
            ngModule: WebsocketModule,
            providers: [
                {
                    provide: WEBSOCKET_CONFIGS,
                    useValue: config
                },
                {
                    provide: WebsocketConfig,
                    useFactory: WebsocketModule.configFactory,
                    deps: [WEBSOCKET_CONFIGS]
                }
            ]
        };
    }
    static configFactory(options) {
        return { ...new WebsocketConfig(), ...options };
    }
}
/** @nocollapse */ WebsocketModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.2", ngImport: i0, type: WebsocketModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
/** @nocollapse */ WebsocketModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "13.3.2", ngImport: i0, type: WebsocketModule });
/** @nocollapse */ WebsocketModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "13.3.2", ngImport: i0, type: WebsocketModule });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.2", ngImport: i0, type: WebsocketModule, decorators: [{
            type: NgModule
        }] });

/**
 * Generated bundle index. Do not edit.
 */

export { AbstractWebsocketClient, BINARY, PLAIN_TEXT, WEBSOCKET_CONFIGS, WebsocketConfig, WebsocketModule };
//# sourceMappingURL=angular-ru-cdk-websocket.mjs.map
