import { Observable } from 'lib0/observable';
import { insertTrustedHTML, injectStylesIntoShadowRoot, formatTimeForSessionTimer } from '../utils';
import { SessionState } from '../types';
import { POPOVER_WIDTH, NON_DRAGGABLE_OFFSET, POPOVER_DISTANCE_FROM_BUTTON, } from './constants';
import { DEFAULT_WIDGET_TEXT_CONFIG } from '../config';
import { isBrowser, isBrowserExtension } from '../global';
import { UIManager } from './UIManager';
import { DragManager } from './dragManager';
import { ButtonState, buttonStates, continuousRecordingSaveButtonStates, } from './buttonStateConfigs';
// Import styles as string for shadow DOM injection
import widgetStyles from '../index.scss?raw';
export class SessionWidget extends Observable {
    set buttonState(newState) {
        var _a, _b;
        this._buttonState = newState;
        if (!this.isBrowser)
            return;
        const { icon, tooltip, classes, excludeClasses } = buttonStates[newState];
        if (newState === ButtonState.CANCEL) {
            (_a = this.buttonDraggabilityObserver) === null || _a === void 0 ? void 0 : _a.observe(this.recorderButton, {
                attributes: true,
                attributeOldValue: true,
                attributeFilter: ['class'],
            });
        }
        else {
            (_b = this.buttonDraggabilityObserver) === null || _b === void 0 ? void 0 : _b.disconnect();
        }
        this.uiManager.setPopoverLoadingState(newState === ButtonState.LOADING);
        this.updateButton(icon, tooltip, excludeClasses, classes);
    }
    set initialPopoverVisible(v) {
        var _a;
        this._initialPopoverVisible = v;
        if (this.isBrowser) {
            (_a = this.initialPopover) === null || _a === void 0 ? void 0 : _a.classList.toggle('hidden', !v);
        }
    }
    set finalPopoverVisible(v) {
        var _a;
        this._finalPopoverVisible = v;
        if (this.isBrowser) {
            (_a = this.finalPopover) === null || _a === void 0 ? void 0 : _a.classList.toggle('hidden', !v);
        }
    }
    get error() {
        return this._error;
    }
    set error(v) {
        this._error = v;
        if (this._error) {
            this.showToast({
                type: 'error',
                message: this._error,
                button: {
                    text: 'Close', onClick: () => this.hideToast(),
                },
            });
        }
    }
    set isStarted(v) {
        this._isStarted = v;
        if (!this.isBrowser)
            return;
        if (this.isBrowserExtension && v && !this._continuousRecording) {
            this.overlay.classList.remove('hidden');
            this.makeOverlayDraggable();
            if (!this.seconds) {
                this.startTimer();
            }
        }
        else {
            this.overlay.classList.add('hidden');
        }
        this.recorderButton.classList.toggle('is-started', this._isStarted);
        if (this._isStarted) {
            if (!this._continuousRecording) {
                this.initialPopoverVisible = false;
                this.buttonState = ButtonState.RECORDING;
            }
            else {
                this.buttonState = ButtonState.CONTINUOUS_DEBUGGING;
            }
        }
        else {
            this.buttonState = ButtonState.IDLE;
        }
    }
    set isPaused(v) {
        this._isPaused = v;
        if (!this.isBrowser)
            return;
        if (this._isInitialized && this.isBrowserExtension && v && !this._continuousRecording) {
            this.overlay.classList.add('hidden');
            this.submitSessionDialog.classList.remove('hidden');
            this.stopTimer();
        }
    }
    constructor() {
        super();
        this._isStarted = false;
        this._isPaused = false;
        this._isInitialized = false;
        this._error = '';
        this._recorderPlacement = '';
        this._showWidget = false;
        this._initialPopoverVisible = false;
        this._finalPopoverVisible = false;
        this._buttonState = ButtonState.IDLE;
        this._continuousRecording = false;
        this._showContinuousRecording = true;
        this._widgetTextOverrides = DEFAULT_WIDGET_TEXT_CONFIG;
        this.commentTextarea = null;
        this.dragManager = null;
        this.buttonClickExternalHandler = null;
        this.seconds = 0;
        this.shadowRoot = null;
        this.hostElement = null;
        this.handleClickOutside = (event) => {
            var _a;
            const target = event.target;
            const isPopoverVisible = this._initialPopoverVisible || this._finalPopoverVisible;
            const popover = this._initialPopoverVisible ? this.initialPopover : this.finalPopover;
            if (!isPopoverVisible || !target)
                return;
            if (target !== this.hostElement &&
                !(popover === null || popover === void 0 ? void 0 : popover.contains(target)) &&
                !((_a = this.recorderButton) === null || _a === void 0 ? void 0 : _a.contains(target)) &&
                target !== this.recorderButton &&
                target !== popover) {
                if (this._initialPopoverVisible) {
                    this.handleCloseInitialPopover();
                }
                else {
                    this.handleCloseFinalPopover();
                }
            }
        };
        this.isBrowser = isBrowser;
        this.isBrowserExtension = isBrowserExtension;
        if (!this.isBrowser) {
            // Create dummy elements for SSR to prevent crashes
            this.uiManager = {};
            this.toast = {};
            this.overlay = {};
            this.finalPopover = {};
            this.initialPopover = {};
            this.submitSessionDialog = {};
            this.recorderButton = {};
            return;
        }
        this.toast = document.createElement('div');
        this.overlay = document.createElement('div');
        this.finalPopover = document.createElement('div');
        this.initialPopover = document.createElement('div');
        this.recorderButton = document.createElement('button');
        this.submitSessionDialog = document.createElement('div');
        this.uiManager = new UIManager(this.recorderButton, this.initialPopover, this.finalPopover, this.overlay, this.submitSessionDialog, this.toast, DEFAULT_WIDGET_TEXT_CONFIG, true);
        this.uiManager.setRecorderButtonProps();
        this.uiManager.setInitialPopoverProps();
        this.uiManager.setFinalPopoverProps();
        this.uiManager.setOverlayProps();
        this.uiManager.setSubmitSessionDialogProps();
        this.uiManager.setToastProps();
        this.commentTextarea = this.finalPopover.querySelector('.mp-session-debugger-popover-textarea');
        this.observeButtonDraggableMode();
    }
    updateState(state, continuousRecording) {
        this._continuousRecording = continuousRecording;
        switch (state) {
            case SessionState.started:
                this.isPaused = false;
                this.isStarted = true;
                break;
            case SessionState.stopped:
                this.isPaused = false;
                this.isStarted = false;
                break;
            case SessionState.paused:
                this.isPaused = true;
                this.isStarted = false;
                break;
            default:
                this.isPaused = false;
                this.isStarted = false;
                break;
        }
    }
    updateContinuousRecordingState(checked, disabled = false) {
        if (!this.isBrowser)
            return;
        const toggleCheckbox = this.initialPopover.querySelector('#mp-session-debugger-continuous-debugging-checkbox');
        if (toggleCheckbox) {
            toggleCheckbox.checked = checked;
            toggleCheckbox.disabled = disabled;
        }
    }
    updateSaveContinuousDebugSessionState(state) {
        if (!this.isBrowser)
            return;
        const saveButton = this.initialPopover.querySelector('#mp-save-continuous-debug-session');
        if (saveButton) {
            const { textContent, disabled } = continuousRecordingSaveButtonStates[state];
            saveButton.disabled = disabled;
            saveButton.textContent = textContent;
        }
    }
    /**
     * Shows a toast message with optional action button
     * @param config - The toast configuration including message, type, and optional button
     * @param duration - Duration in milliseconds to show the toast (default: 10000ms)
     */
    showToast(config, duration = 10000) {
        if (!this.isBrowser)
            return;
        this.uiManager.showToast(config, duration);
    }
    /**
     * Hides the currently displayed toast message
     */
    hideToast() {
        if (!this.isBrowser)
            return;
        this.uiManager.hideToast();
    }
    observeButtonDraggableMode() {
        if (!this.isBrowser)
            return;
        this.buttonDraggabilityObserver = new MutationObserver((mutationsList) => {
            for (const mutation of mutationsList) {
                if (mutation.type === 'attributes' &&
                    mutation.attributeName === 'class') {
                    const oldClassName = mutation.oldValue;
                    const newClassName = mutation.target['className'];
                    if (((oldClassName === null || oldClassName === void 0 ? void 0 : oldClassName.includes('no-draggable')) &&
                        !newClassName.includes('no-draggable')) ||
                        ((newClassName === null || newClassName === void 0 ? void 0 : newClassName.includes('no-draggable')) &&
                            !(oldClassName === null || oldClassName === void 0 ? void 0 : oldClassName.includes('no-draggable')))) {
                        // draggable mode was changed
                        this.initialPopoverVisible = false;
                        this.finalPopoverVisible = false;
                    }
                }
            }
        });
    }
    init(options) {
        if (this._isInitialized)
            return;
        if (!this.isBrowser)
            return;
        this._isInitialized = true;
        this._showWidget = options.showWidget;
        this._showContinuousRecording = options.showContinuousRecording;
        this._widgetTextOverrides = {
            ...this._widgetTextOverrides,
            ...options.widgetTextOverrides,
        };
        // Recreate UIManager with proper config
        this.uiManager = new UIManager(this.recorderButton, this.initialPopover, this.finalPopover, this.overlay, this.submitSessionDialog, this.toast, this._widgetTextOverrides, this._showContinuousRecording);
        // Re-initialize templates with new config
        this.uiManager.setRecorderButtonProps();
        this.uiManager.setInitialPopoverProps();
        this.uiManager.setFinalPopoverProps();
        this.uiManager.setOverlayProps();
        this.uiManager.setSubmitSessionDialogProps();
        this.uiManager.setToastProps();
        const elements = [this.toast];
        if (options.showWidget) {
            elements.push(this.recorderButton, this.initialPopover, this.finalPopover, this.submitSessionDialog);
        }
        else {
            elements.push(this.overlay, this.submitSessionDialog);
        }
        this.appendElements(elements);
        // Hide continuous recording UI when feature is disabled
        if (!this._showContinuousRecording) {
            const cont = this.initialPopover.querySelector('.mp-session-debugger-continuous-debugging');
            cont && cont.classList.add('hidden');
            const overlay = this.initialPopover.querySelector('.mp-session-debugger-continuous-debugging-overlay');
            overlay && overlay.classList.add('hidden');
        }
        if (options.showWidget && options.widgetButtonPlacement) {
            this.recorderButton.classList.add(options.widgetButtonPlacement);
            this._recorderPlacement = options.widgetButtonPlacement;
            this.addRecorderDragFunctionality();
        }
        this.addEventListeners();
    }
    appendElements(elements) {
        if (!this.isBrowser || typeof document === 'undefined')
            return;
        // Create host element
        const rootWrapper = document.createElement('mp-root');
        rootWrapper.classList.add('mp-root-wrapper');
        rootWrapper.setAttribute('data-rr-ignore', 'true');
        this.hostElement = rootWrapper;
        // Create shadow root
        this.shadowRoot = rootWrapper.attachShadow({ mode: 'open' });
        // Inject styles directly from imported SCSS file
        if (widgetStyles) {
            injectStylesIntoShadowRoot(this.shadowRoot, widgetStyles);
        }
        // Append all elements to shadow root
        elements.forEach((element) => this.shadowRoot.appendChild(element));
        // Append host element to document body
        document.body.appendChild(rootWrapper);
    }
    addRecorderDragFunctionality() {
        if (!this.isBrowser)
            return;
        this.dragManager = new DragManager(this.recorderButton, this._recorderPlacement, () => {
            if (this._isPaused) {
                this.finalPopoverVisible = true;
            }
        }, () => this.updatePopoverPosition(), (e) => this.onRecordingButtonClick(e));
        this.dragManager.init();
    }
    updatePopoverPosition() {
        if (!this.isBrowser || typeof window === 'undefined')
            return;
        const { top, right, bottom, left } = this.recorderButton.getBoundingClientRect();
        const isDraggable = !this.recorderButton.classList.contains('no-draggable');
        const POPOVER_HEIGHT = this._isStarted ? 400 : 300;
        const VIEWPORT_WIDTH = window.innerWidth;
        const VIEWPORT_HEIGHT = window.innerHeight;
        let popoverBottom;
        let popoverRight;
        popoverBottom = VIEWPORT_HEIGHT - top + POPOVER_DISTANCE_FROM_BUTTON + (isDraggable ? 0 : NON_DRAGGABLE_OFFSET);
        popoverRight = VIEWPORT_WIDTH - right;
        if (popoverBottom + POPOVER_HEIGHT > VIEWPORT_HEIGHT) {
            popoverBottom = VIEWPORT_HEIGHT - bottom - POPOVER_HEIGHT - POPOVER_DISTANCE_FROM_BUTTON - (isDraggable ? 0 : NON_DRAGGABLE_OFFSET);
        }
        if (popoverRight + POPOVER_WIDTH > VIEWPORT_WIDTH) {
            popoverRight = VIEWPORT_WIDTH - left - POPOVER_WIDTH;
        }
        const MIN_MARGIN = 10;
        popoverBottom = Math.max(popoverBottom, MIN_MARGIN);
        popoverRight = Math.max(popoverRight, MIN_MARGIN);
        if (popoverRight + POPOVER_WIDTH > VIEWPORT_WIDTH - MIN_MARGIN) {
            popoverRight = VIEWPORT_WIDTH - POPOVER_WIDTH - MIN_MARGIN;
        }
        if (popoverBottom + POPOVER_HEIGHT > VIEWPORT_HEIGHT - MIN_MARGIN) {
            popoverBottom = VIEWPORT_HEIGHT - POPOVER_HEIGHT - MIN_MARGIN;
        }
        const updatePopoverStyles = (popover) => {
            popover.style.right = `${popoverRight}px`;
            popover.style.bottom = `${popoverBottom}px`;
            popover.style.left = 'unset';
            popover.style.top = 'unset';
        };
        requestAnimationFrame(() => {
            this.initialPopover && updatePopoverStyles(this.initialPopover);
            this.finalPopover && updatePopoverStyles(this.finalPopover);
        });
    }
    addEventListeners() {
        if (!this.isBrowser)
            return;
        const events = [];
        if (this.isBrowserExtension) {
            events.push({
                target: this.overlay,
                selector: '.mp-stop-btn',
                handler: this.onPause.bind(this), // change to submit dialog
            }, {
                target: this.submitSessionDialog,
                selector: '#mp-submit-recording',
                handler: this.onStop.bind(this),
            }, {
                target: this.submitSessionDialog,
                selector: '#mp-cancel-submission',
                handler: this.onCancel.bind(this),
            });
        }
        if (this._showWidget) {
            events.push({
                target: this.initialPopover,
                selector: '.mp-start-recording',
                handler: this.startRecording.bind(this),
            });
            if (this._showContinuousRecording) {
                events.push({
                    event: 'change',
                    target: this.initialPopover,
                    selector: '#mp-session-debugger-continuous-debugging-checkbox',
                    handler: this.handleContinuousRecordingChange.bind(this),
                }, {
                    target: this.initialPopover,
                    selector: '#mp-save-continuous-debug-session',
                    handler: this.handleSaveContinuousDebugSession.bind(this),
                });
            }
            events.push({
                target: this.initialPopover,
                selector: '.mp-session-debugger-modal-close',
                handler: this.handleCloseInitialPopover.bind(this),
            }, {
                target: this.finalPopover,
                selector: '.mp-stop-recording',
                handler: this.handleStopRecording.bind(this),
            }, {
                target: this.finalPopover,
                selector: '.mp-session-debugger-dismiss-button',
                handler: this.handleDismissRecording.bind(this),
            }, {
                target: this.finalPopover,
                selector: '.mp-session-debugger-modal-close',
                handler: this.handleCloseFinalPopover.bind(this),
            });
        }
        events.forEach(({ target, selector, handler, event = 'click' }) => {
            this.addListener(target, selector, handler, event);
        });
    }
    handleStopRecording() {
        if (!this.isBrowser)
            return;
        this.onStop();
        this.handleUIReseting();
    }
    handleUIReseting() {
        if (!this.isBrowser)
            return;
        this.finalPopoverVisible = false;
        this.resetRecordingButton();
    }
    handleCloseInitialPopover() {
        if (!this.isBrowser)
            return;
        if (this._buttonState === ButtonState.LOADING) {
            this.onCancel();
        }
        this.initialPopoverVisible = false;
        this.buttonState = this._continuousRecording
            ? ButtonState.CONTINUOUS_DEBUGGING
            : ButtonState.IDLE;
        if (typeof document !== 'undefined') {
            document.removeEventListener('click', this.handleClickOutside);
        }
    }
    handleCloseFinalPopover() {
        this.onResume();
    }
    onRequestError() {
        if (!this.isBrowser)
            return;
        this.initialPopoverVisible = false;
        this.finalPopoverVisible = false;
        this.buttonState = ButtonState.IDLE;
        if (typeof document !== 'undefined') {
            document.removeEventListener('click', this.handleClickOutside);
        }
    }
    handleDismissRecording() {
        if (!this.isBrowser)
            return;
        this.onCancel();
        this.finalPopoverVisible = !this._finalPopoverVisible;
        this.buttonState = ButtonState.IDLE;
        this.overlay.classList.add('hidden');
        if (this.commentTextarea) {
            this.commentTextarea.value = '';
        }
    }
    resetRecordingButton() {
        setTimeout(() => {
            this.buttonState = ButtonState.IDLE;
        }, 1500);
    }
    addListener(element, selector, handler, event = 'click') {
        var _a;
        (_a = element === null || element === void 0 ? void 0 : element.querySelector(selector)) === null || _a === void 0 ? void 0 : _a.addEventListener(event, handler);
    }
    onRecordingButtonClick(e) {
        if (!this.isBrowser)
            return;
        if (this.buttonClickExternalHandler) {
            const shouldPropagate = this.buttonClickExternalHandler();
            if (shouldPropagate === false) {
                e.preventDefault();
                return;
            }
        }
        if (this._initialPopoverVisible) {
            this.handleCloseInitialPopover();
            return;
        }
        if (this._isPaused) {
            this.onResume();
            return;
        }
        if (this._isStarted) {
            this.buttonState = ButtonState.CANCEL;
            if (this._continuousRecording) {
                this.initialPopoverVisible = !this.initialPopoverVisible;
            }
            else {
                this.finalPopoverVisible = !this._finalPopoverVisible;
                this.onPause();
            }
        }
        else {
            this.buttonState = this._initialPopoverVisible
                ? ButtonState.IDLE
                : ButtonState.CANCEL;
            this.initialPopoverVisible = !this._initialPopoverVisible;
        }
        if (typeof document !== 'undefined') {
            if (this._initialPopoverVisible || this._finalPopoverVisible) {
                document.addEventListener('click', this.handleClickOutside);
            }
            else {
                document.removeEventListener('click', this.handleClickOutside);
            }
        }
    }
    updateButton(innerHTML, tooltip, excludeClasses, classes) {
        if (!this.isBrowser || !this.recorderButton)
            return;
        insertTrustedHTML(this.recorderButton, `${innerHTML}`);
        this.recorderButton.dataset['tooltip'] = tooltip;
        if (excludeClasses) {
            this.recorderButton.classList.remove(...excludeClasses);
        }
        if (classes) {
            this.recorderButton.classList.add(...classes);
        }
    }
    handleContinuousRecordingChange(e) {
        if (!this._showContinuousRecording)
            return;
        const checkbox = e.target;
        this.emit('continuous-debugging', [checkbox.checked]);
    }
    handleSaveContinuousDebugSession() {
        this.emit('save', []);
    }
    startRecording() {
        this.buttonState = ButtonState.LOADING;
        this.onStart();
    }
    onStart() {
        if (!this.recorderButton)
            return;
        this.emit('start', []);
    }
    onStop() {
        if (!this.isBrowser)
            return;
        if (this._showWidget && !this.recorderButton)
            return;
        let commentElement = null;
        if (this.isBrowserExtension) {
            this.submitSessionDialog.classList.add('hidden');
            commentElement = this.submitSessionDialog.querySelector('#mp-recording-comment');
        }
        else {
            commentElement = this.commentTextarea;
        }
        if (commentElement) {
            this.emit('stop', [commentElement.value]);
            commentElement.value = '';
        }
        else {
            this.emit('stop', []);
        }
    }
    onPause() {
        this.emit('pause', []);
    }
    onResume() {
        if (!this.isBrowser)
            return;
        this.finalPopoverVisible = false;
        if (!this._continuousRecording) {
            this.buttonState = ButtonState.RECORDING;
            this.emit('resume', []);
        }
        else {
            this.buttonState = ButtonState.CONTINUOUS_DEBUGGING;
        }
    }
    onCancel() {
        if (!this.isBrowser)
            return;
        this.submitSessionDialog.classList.add('hidden');
        this.emit('cancel', []);
    }
    enable() {
        if (!this.isBrowser || !this.recorderButton)
            return;
        this.recorderButton.disabled = false;
        this.recorderButton.style.opacity = '1';
    }
    disable() {
        if (!this.isBrowser || !this.recorderButton)
            return;
        this.recorderButton.disabled = true;
        this.recorderButton.style.opacity = '0.5';
    }
    destroy() {
        if (!this.isBrowser || typeof document === 'undefined')
            return;
        const rootWrapper = this.hostElement || document.querySelector('.mp-root-wrapper');
        if (rootWrapper && rootWrapper.parentNode) {
            rootWrapper.parentNode.removeChild(rootWrapper);
        }
        this.shadowRoot = null;
        this.hostElement = null;
        document.removeEventListener('click', this.handleClickOutside);
    }
    startTimer() {
        if (!this.isBrowser)
            return;
        if (this.timerInterval) {
            clearInterval(this.timerInterval);
            this.timerInterval = null;
        }
        this.uiManager.setTimerValue(formatTimeForSessionTimer(this.seconds));
        this.timerInterval = setInterval(() => {
            this.seconds++;
            this.uiManager.setTimerValue(formatTimeForSessionTimer(this.seconds));
        }, 1000);
    }
    stopTimer() {
        if (this.timerInterval) {
            clearInterval(this.timerInterval);
            this.timerInterval = null;
            this.seconds = 0;
        }
    }
    makeOverlayDraggable() {
        if (!this.isBrowser || typeof document === 'undefined')
            return;
        const element = this.overlay;
        const dragHandle = element.querySelector('.mp-drag-handle');
        if (!dragHandle)
            return;
        let offsetX = 0, offsetY = 0;
        dragHandle.onmousedown = function (e) {
            e.preventDefault();
            offsetX = e.clientX - element.offsetLeft;
            offsetY = e.clientY - element.offsetTop;
            document.onmousemove = function (e) {
                element.style.left = `${e.clientX - offsetX}px`;
                element.style.top = `${e.clientY - offsetY}px`;
                element.style.bottom = 'auto';
                element.style.transform = 'none';
            };
            document.onmouseup = function () {
                document.onmousemove = null;
                document.onmouseup = null;
            };
        };
    }
}
//# sourceMappingURL=index.js.map