/**
 * DOM 조작 유틸리티 모듈 (DOMUtils.js)
 * ========================================
 * document.querySelector 대신 쓰는 간편 함수들입니다.
 *
 * [초보자용 사용 예]
 * DOMUtils.$('.my-class')           → 첫 번째 요소 (querySelector)
 * DOMUtils.$$('.my-class')          → 모든 요소 (querySelectorAll)
 * DOMUtils.fadeIn(el, 300)          → 페이드 인 (300ms)
 * DOMUtils.fadeOut(el, 300)         → 페이드 아웃
 * DOMUtils.delegate(parent, 'click', '.btn', handler)  → 동적 요소에 이벤트 위임
 * DOMUtils.htmlToElement('<div>')   → HTML 문자열 → Element
 *
 * @module DOMUtils
 */
class DOMUtils {
  /**
   * 요소 선택 (단일)
   * @param {string} selector - CSS 선택자
   * @param {Element} parent - 부모 요소
   * @returns {Element|null}
   */
  static $(selector, parent = document) {
    return parent.querySelector(selector);
  }

  /**
   * 요소 선택 (다중)
   * @param {string} selector - CSS 선택자
   * @param {Element} parent - 부모 요소
   * @returns {NodeList}
   */
  static $$(selector, parent = document) {
    return parent.querySelectorAll(selector);
  }

  /**
   * 요소 생성
   * @param {string} tag - 태그명
   * @param {Object} attrs - 속성 객체
   * @param {string} content - 내용
   * @returns {Element}
   */
  static createElement(tag, attrs = {}, content = "") {
    const element = document.createElement(tag);

    Object.entries(attrs).forEach(([key, value]) => {
      if (key === "class" || key === "className") {
        element.className = value;
      } else if (key === "style" && typeof value === "object") {
        Object.assign(element.style, value);
      } else if (key.startsWith("data-")) {
        element.setAttribute(key, value);
      } else {
        element[key] = value;
      }
    });

    if (content) {
      if (typeof content === "string") {
        element.innerHTML = content;
      } else if (content instanceof Node) {
        element.appendChild(content);
      }
    }

    return element;
  }

  /**
   * 클래스 토글
   * @param {Element} element - 대상 요소
   * @param {string} className - 클래스명
   * @param {boolean} force - 강제 적용 여부
   */
  static toggleClass(element, className, force) {
    if (!element) return;
    if (force !== undefined) {
      element.classList.toggle(className, force);
    } else {
      element.classList.toggle(className);
    }
  }

  /**
   * 여러 클래스 추가
   * @param {Element} element - 대상 요소
   * @param {...string} classNames - 클래스명들
   */
  static addClasses(element, ...classNames) {
    if (!element) return;
    element.classList.add(...classNames);
  }

  /**
   * 여러 클래스 제거
   * @param {Element} element - 대상 요소
   * @param {...string} classNames - 클래스명들
   */
  static removeClasses(element, ...classNames) {
    if (!element) return;
    element.classList.remove(...classNames);
  }

  /**
   * 요소의 위치 정보 가져오기
   * @param {Element} element - 대상 요소
   * @returns {Object}
   */
  static getPosition(element) {
    if (!element) return null;
    const rect = element.getBoundingClientRect();
    return {
      top: rect.top,
      left: rect.left,
      right: rect.right,
      bottom: rect.bottom,
      width: rect.width,
      height: rect.height,
      x: rect.x,
      y: rect.y,
    };
  }

  /**
   * 요소를 퍼센트 위치로 설정
   * @param {Element} element - 대상 요소
   * @param {number} x - X 위치 (%)
   * @param {number} y - Y 위치 (%)
   * @param {string} transform - 추가 transform
   */
  static setPercentPosition(element, x, y, transform = "translate(-50%, -50%)") {
    if (!element) return;
    element.style.position = "absolute";
    element.style.left = `${x}%`;
    element.style.top = `${y}%`;
    if (transform) {
      element.style.transform = transform;
    }
  }

  /**
   * 요소 페이드 인
   * @param {Element} element - 대상 요소
   * @param {number} duration - 지속 시간 (ms)
   * @returns {Promise}
   */
  static fadeIn(element, duration = 300) {
    if (!element) return Promise.resolve();

    return new Promise((resolve) => {
      // 기존 display 값 저장 (grid, flex 등 유지)
      const originalDisplay = element.style.display || window.getComputedStyle(element).display;
      const isGridOrFlex = originalDisplay === "grid" || originalDisplay === "flex" || 
                          originalDisplay.includes("grid") || originalDisplay.includes("flex");

      element.style.opacity = "0";
      // grid/flex인 경우 display를 설정하지 않음
      if (!isGridOrFlex) {
        element.style.display = "block";
      }
      element.style.transition = `opacity ${duration}ms ease-in-out`;

      setTimeout(() => {
        element.style.opacity = "1";
      }, 10);

      setTimeout(() => {
        element.style.transition = "";
        // grid/flex인 경우 display 스타일 제거
        if (isGridOrFlex) {
          element.style.display = "";
        }
        resolve();
      }, duration);
    });
  }

  /**
   * 요소 페이드 아웃
   * @param {Element} element - 대상 요소
   * @param {number} duration - 지속 시간 (ms)
   * @returns {Promise}
   */
  static fadeOut(element, duration = 300) {
    if (!element) return Promise.resolve();

    return new Promise((resolve) => {
      element.style.opacity = "1";
      element.style.transition = `opacity ${duration}ms ease-in-out`;

      setTimeout(() => {
        element.style.opacity = "0";
      }, 10);

      setTimeout(() => {
        element.style.display = "none";
        element.style.transition = "";
        element.style.opacity = ""; // 재오픈 시 opacity 초기화
        resolve();
      }, duration);
    });
  }

  /**
   * 요소 슬라이드 다운
   * @param {Element} element - 대상 요소
   * @param {number} duration - 지속 시간 (ms)
   * @returns {Promise}
   */
  static slideDown(element, duration = 300) {
    if (!element) return Promise.resolve();

    return new Promise((resolve) => {
      element.style.display = "block";
      const height = element.scrollHeight;
      element.style.height = "0";
      element.style.overflow = "hidden";
      element.style.transition = `height ${duration}ms ease-in-out`;

      setTimeout(() => {
        element.style.height = `${height}px`;
      }, 10);

      setTimeout(() => {
        element.style.height = "";
        element.style.overflow = "";
        element.style.transition = "";
        resolve();
      }, duration);
    });
  }

  /**
   * 요소 슬라이드 업
   * @param {Element} element - 대상 요소
   * @param {number} duration - 지속 시간 (ms)
   * @returns {Promise}
   */
  static slideUp(element, duration = 300) {
    if (!element) return Promise.resolve();

    return new Promise((resolve) => {
      const height = element.scrollHeight;
      element.style.height = `${height}px`;
      element.style.overflow = "hidden";
      element.style.transition = `height ${duration}ms ease-in-out`;

      setTimeout(() => {
        element.style.height = "0";
      }, 10);

      setTimeout(() => {
        element.style.display = "none";
        element.style.height = "";
        element.style.overflow = "";
        element.style.transition = "";
        resolve();
      }, duration);
    });
  }

  /**
   * 이벤트 위임
   * @param {Element} parent - 부모 요소
   * @param {string} eventType - 이벤트 타입
   * @param {string} selector - 자식 선택자
   * @param {Function} handler - 핸들러 함수
   */
  static delegate(parent, eventType, selector, handler) {
    if (!parent) return;

    parent.addEventListener(eventType, (event) => {
      const target = event.target.closest(selector);
      if (target && parent.contains(target)) {
        handler.call(target, event);
      }
    });
  }

  /**
   * HTML 문자열을 요소로 변환
   * @param {string} html - HTML 문자열
   * @returns {Element}
   */
  static htmlToElement(html) {
    const template = document.createElement("template");
    template.innerHTML = html.trim();
    return template.content.firstElementChild;
  }

  /**
   * HTML 문자열을 요소 배열로 변환
   * @param {string} html - HTML 문자열
   * @returns {Array}
   */
  static htmlToElements(html) {
    const template = document.createElement("template");
    template.innerHTML = html.trim();
    return Array.from(template.content.children);
  }

  /**
   * 요소가 뷰포트에 있는지 확인
   * @param {Element} element - 대상 요소
   * @returns {boolean}
   */
  static isInViewport(element) {
    if (!element) return false;
    const rect = element.getBoundingClientRect();
    return (
      rect.top >= 0 &&
      rect.left >= 0 &&
      rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
      rect.right <= (window.innerWidth || document.documentElement.clientWidth)
    );
  }

  /**
   * 부드러운 스크롤
   * @param {Element|string} target - 대상 요소 또는 선택자
   * @param {Object} options - 옵션
   */
  static smoothScroll(target, options = {}) {
    const element = typeof target === "string" ? this.$(target) : target;
    if (!element) return;

    const defaultOptions = {
      behavior: "smooth",
      block: "start",
      inline: "nearest",
    };

    element.scrollIntoView({ ...defaultOptions, ...options });
  }

  /**
   * 전체 화면 토글
   * @param {Element} element - 대상 요소
   */
  static toggleFullscreen(element = document.documentElement) {
    if (!document.fullscreenElement) {
      element.requestFullscreen?.() ||
        element.webkitRequestFullscreen?.() ||
        element.msRequestFullscreen?.();
    } else {
      document.exitFullscreen?.() ||
        document.webkitExitFullscreen?.() ||
        document.msExitFullscreen?.();
    }
  }

  /**
   * 클립보드에 복사
   * @param {string} text - 복사할 텍스트
   * @returns {Promise}
   */
  static async copyToClipboard(text) {
    try {
      await navigator.clipboard.writeText(text);
      return true;
    } catch (err) {
      console.error("Failed to copy:", err);
      return false;
    }
  }

  /**
   * 요소의 스타일 가져오기
   * @param {Element} element - 대상 요소
   * @param {string} property - CSS 속성
   * @returns {string}
   */
  static getStyle(element, property) {
    if (!element) return null;
    return window.getComputedStyle(element).getPropertyValue(property);
  }

  /**
   * 여러 스타일 설정
   * @param {Element} element - 대상 요소
   * @param {Object} styles - 스타일 객체
   */
  static setStyles(element, styles) {
    if (!element || !styles) return;
    Object.assign(element.style, styles);
  }

  /**
   * 요소 제거
   * @param {Element} element - 대상 요소
   */
  static remove(element) {
    if (element && element.parentNode) {
      element.parentNode.removeChild(element);
    }
  }

  /**
   * 요소 내용 비우기
   * @param {Element} element - 대상 요소
   */
  static empty(element) {
    if (!element) return;
    while (element.firstChild) {
      element.removeChild(element.firstChild);
    }
  }
}

// ES6 모듈 내보내기
if (typeof module !== "undefined" && module.exports) {
  module.exports = DOMUtils;
}
