/* eslint-disable jsdoc/require-returns */
import { setElementStyles } from '@utils/dom';
 
/**
 * 전체 화면을 덮는 `레이어` 입니다.
 * @param {Object} options 레이어 옵션
 * @param {string} [options.rootId='js-layer'] 레이어의 루트 요소 ID
 * @param {string} [options.zIndex='110000'] z-index 값
 * @param {string} [options.backgroundColor='rgba(0,0,0,.5)'] 배경색
 * @param {(contentEl: HTMLElement) => void} [options.mountContent] 컨텐츠를 마운트하는 함수
 * @param {(error: Error) => void} [options.onMountError] 에러 발생 시 호출될 콜백 함수
 * @param {Object} [options.contentStyle] 컨텐츠 요소에 적용할 추가 스타일
 * @example
 * import { generateLayer } from '@components/common/Layer';
 *
 * // LoadingLayer.js
 * const LoadingLayer = generateLayer({...});
 *
 * // 사용법
 * LoadingLayer.show()
 * LoadingLayer.hide()
 */
export const generateLayer = ({
    rootId = 'js-layer',
    zIndex = '110000',
    backgroundColor = 'rgba(0,0,0,.5)',
    mountContent = (contentEl) => console.log(contentEl),
    onMountError = (error) => console.error('Layer error:', error),
    contentStyle = {},
}) => {
    let isOpen = false;
    let root = null;
    let content = null;
    let hideTimer = null;
    let removeBackdropClickListener = null;
 
    const listenBackdropEvent = (callback) => {
        const handler = () => {
            if (typeof callback === 'function') {
                callback();
            } else {
                hide();
            }
        };
 
        const onClickBackdrop = (e) => {
            if (e.target === root) {
                handler();
            }
        };
 
        const onKeydownEsc = (e) => {
            if (e.key === 'Escape') {
                handler();
            }
        };
 
        root.addEventListener('click', onClickBackdrop);
        window.addEventListener('keydown', onKeydownEsc);
 
        removeBackdropClickListener = () => {
            root.removeEventListener('click', onClickBackdrop);
            window.removeEventListener('keydown', onKeydownEsc);
        };
    };
 
    const cleanupListenBackdropEvent = () => {
        removeBackdropClickListener?.();
        removeBackdropClickListener = null;
    };
 
    const mount = () => {
        try {
            document.querySelectorAll(`#${rootId}`).forEach((existRoot) => {
                existRoot.remove();
            });
 
            root = document.createElement('div');
            root.setAttribute('id', rootId);
 
            content = document.createElement('div');
            setElementStyles(content, {
                zIndex,
                position: 'absolute',
                top: '50%',
                left: '50%',
                transform: 'translate(-50%, -50%)',
                opacity: '0',
                transitionProperty: 'opacity',
                transitionTimingFunction: 'ease-out',
                ...contentStyle,
            });
            mountContent(content);
            root.appendChild(content);
 
            document.body.appendChild(root);
        } catch (error) {
            onMountError(error);
            destroy();
        }
    };
 
    /**
     * 다이얼로그 레이어를 표시합니다.
     * @param {Object} option 레이어 표시 옵션
     * @param {number} [option.duration=170] 애니메이션 지속 시간 (밀리초)
     * @param {boolean} [option.hasBackdrop=true] 배경 레이어를 표시할지 여부
     * @param {boolean|function} [option.onBackdropEvent=false] 배경 레이어 클릭 및 esc 입력시 수행하는 이벤트, true 시 레이어를 숨기는 액션이 수행됩니다.
     */
    const show = ({
        duration = 170,
        hasBackdrop = true,
        onBackdropEvent = false,
    } = {}) => {
        if (isOpen) {
            return;
        }
 
        if (!root) {
            mount();
        }
 
        if (hasBackdrop) {
            setElementStyles(root, {
                zIndex,
                position: 'fixed',
                top: '0',
                bottom: '0',
                left: '0',
                right: '0',
                backgroundColor: 'rgba(0,0,0,0)',
                transitionProperty: 'background-color',
                transitionDuration: `${duration}ms`,
                transitionTimingFunction: 'ease-out',
            });
        } else {
            root.removeAttribute('style');
        }
 
        isOpen = true;
        root.style.display = 'block';
        content.style.transitionDuration = `${duration}ms`;
        requestAnimationFrame(() => {
            root.style.backgroundColor = backgroundColor;
            content.style.opacity = '1';
        });
 
        cleanupListenBackdropEvent();
 
        if (onBackdropEvent) {
            listenBackdropEvent(onBackdropEvent);
        }
    };
 
    const cleanupHideTimer = () => {
        if (hideTimer) {
            clearTimeout(hideTimer);
            hideTimer = null;
        }
    };
 
    /**
     * 레이어를 숨깁니다.
     * @param {Object} option - 레이어 숨김 옵션
     * @param {number} [option.duration=150] - 애니메이션 지속 시간 (밀리초)
     */
    const hide = ({
        duration = 150,
    } = {}) => {
        if (!root || !isOpen) {
            return;
        }
 
        isOpen = false;
        cleanupListenBackdropEvent();
        cleanupHideTimer();
 
        requestAnimationFrame(() => {
            setElementStyles(root, {
                backgroundColor: 'rgba(0,0,0,0)',
                transitionDuration: `${duration}ms`,
            });
            setElementStyles(content, {
                opacity: '0',
                transitionDuration: `${duration}ms`,
            });
            hideTimer = setTimeout(() => {
                root.style.display = 'none';
                hideTimer = null;
            }, duration);
        });
    };
 
    const destroy = () => {
        if (!root) {
            return;
        }
 
        isOpen = false;
        cleanupListenBackdropEvent();
        cleanupHideTimer();
        root.remove();
        root = null;
        content = null;
    };
 
    return {
        show,
        hide,
        destroy,
    };
};