import { trace, SpanStatusCode } from '@opentelemetry/api';
import { SessionType } from '@multiplayer-app/session-recorder-common';
const DEFAULT_CONFIG = {
    enabled: true,
};
const DEFAULT_FRAMEWORK = 'web';
const DEFAULT_SOURCE = 'router';
export class NavigationRecorder {
    constructor() {
        this.config = { ...DEFAULT_CONFIG };
        this.isRecording = false;
        this.currentRoute = null;
        this.stack = [];
        this.navigationStartTime = 0;
        this.sessionContext = {
            sessionId: null,
            sessionType: SessionType.MANUAL,
        };
        this.api = {
            record: (signal) => this.record(signal),
            reset: () => this.reset(),
            getCurrentRoute: () => this.currentRoute,
            getStack: () => [...this.stack],
        };
    }
    init(config) {
        this.config = { ...DEFAULT_CONFIG, ...config };
    }
    start(context) {
        var _a, _b;
        if (!this.config.enabled) {
            return;
        }
        this.sessionContext = {
            sessionId: (_a = context === null || context === void 0 ? void 0 : context.sessionId) !== null && _a !== void 0 ? _a : null,
            sessionType: (_b = context === null || context === void 0 ? void 0 : context.sessionType) !== null && _b !== void 0 ? _b : SessionType.MANUAL,
        };
        this.isRecording = true;
        this.navigationStartTime = Date.now();
    }
    pause() {
        this.isRecording = false;
    }
    resume() {
        if (!this.config.enabled) {
            return;
        }
        this.isRecording = true;
        this.navigationStartTime = Date.now();
    }
    stop() {
        this.isRecording = false;
        this.reset();
    }
    record(signal) {
        var _a;
        if (!this.config.enabled || !this.isRecording) {
            return;
        }
        const timestamp = (_a = signal.timestamp) !== null && _a !== void 0 ? _a : Date.now();
        const routeKey = this.resolveRouteKey(signal);
        const previousRoute = this.currentRoute;
        if (routeKey) {
            this.updateNavigationStack(routeKey, signal.navigationType);
            this.currentRoute = routeKey;
        }
        const metadata = {
            ...(signal.metadata || {}),
            framework: signal.framework || DEFAULT_FRAMEWORK,
            source: signal.source || DEFAULT_SOURCE,
            navigationType: signal.navigationType || (previousRoute ? 'push' : 'initial'),
            navigationDuration: timestamp - this.navigationStartTime,
            stackDepth: this.stack.length,
        };
        if (previousRoute) {
            metadata.previousRoute = previousRoute;
        }
        if (signal.path) {
            metadata.path = signal.path;
        }
        if (signal.title) {
            metadata.documentTitle = signal.title;
        }
        if (signal.url) {
            metadata.url = signal.url;
        }
        if (signal.state !== undefined) {
            metadata.state = this.stringifySafe(signal.state);
        }
        const paramsString = signal.params ? this.stringifySafe(signal.params) : undefined;
        this.recordSpan({
            timestamp,
            routeName: signal.routeName || routeKey,
            paramsString,
            metadata,
        });
        this.navigationStartTime = timestamp;
    }
    recordSpan(payload) {
        try {
            const span = trace.getTracer('navigation').startSpan('Navigation.navigate', {
                startTime: payload.timestamp,
                attributes: {
                    'navigation.system': 'router',
                    'navigation.operation': 'navigate',
                    'navigation.type': 'navigate',
                    'navigation.platform': 'web',
                },
            });
            if (payload.routeName) {
                span.setAttribute('navigation.route_name', payload.routeName);
            }
            if (payload.paramsString) {
                span.setAttribute('navigation.params', payload.paramsString);
            }
            Object.entries(payload.metadata).forEach(([key, value]) => {
                const normalizedValue = typeof value === 'string' ? value : this.stringifySafe(value);
                span.setAttribute(`navigation.metadata.${key}`, normalizedValue);
            });
            span.setStatus({ code: SpanStatusCode.OK });
            span.end(payload.timestamp);
        }
        catch (error) {
            if (process.env.NODE_ENV !== 'production') {
                // eslint-disable-next-line no-console
                console.warn('[SessionRecorder][NavigationRecorder] Failed to record navigation span', error);
            }
        }
    }
    updateNavigationStack(routeName, navigationType) {
        const normalized = (navigationType || '').toLowerCase();
        if (normalized === 'replace') {
            if (this.stack.length) {
                this.stack[this.stack.length - 1] = routeName;
            }
            else {
                this.stack.push(routeName);
            }
            return;
        }
        if (normalized === 'pop' || normalized === 'back' || normalized === 'goback') {
            const index = this.stack.lastIndexOf(routeName);
            if (index >= 0) {
                this.stack = this.stack.slice(0, index + 1);
            }
            else if (this.stack.length) {
                this.stack.pop();
            }
            return;
        }
        if (!this.stack.length || this.stack[this.stack.length - 1] !== routeName) {
            this.stack.push(routeName);
        }
    }
    resolveRouteKey(signal) {
        if (signal.routeName && typeof signal.routeName === 'string') {
            return signal.routeName;
        }
        if (signal.path && typeof signal.path === 'string') {
            return signal.path;
        }
        if (signal.url && typeof signal.url === 'string') {
            try {
                return new URL(signal.url).pathname || signal.url;
            }
            catch (_error) {
                return signal.url;
            }
        }
        if (signal.title && typeof signal.title === 'string') {
            return signal.title;
        }
        return 'unknown';
    }
    stringifySafe(value) {
        try {
            return JSON.stringify(value);
        }
        catch (_error) {
            return String(value);
        }
    }
    reset() {
        this.stack = [];
        this.currentRoute = null;
        this.navigationStartTime = Date.now();
    }
}
//# sourceMappingURL=navigationRecorder.js.map