import Swiper from './swiper-bundle.esm.browser.min.js';
import { CountUp } from './countUp.min.js';

/** 屏幕宽度 */
export const WINDTH = innerWidth;
/** 屏幕高度 */
export const HEIGHT = innerHeight;

export function onEnter(inputEl, callback) {
  $(inputEl).focus((e) => {
    $(document).keyup((e) => {
      if (e.keyCode == 13) {
        const value = $(inputEl).val();
        callback(value);
      }
    });
  });

  $(inputEl).focusout(() => {
    $(document).off('keyup');
  });
}

export function gsapAni() {
  if (!window.gsap) return;
  gsap.registerPlugin(ScrollTrigger);

  let bottom = 100;
  if (WINDTH < 980) {
    bottom = 20;
  }

  ScrollTrigger.batch('[data-scroll]', {
    toggleClass: 'inview',
    start: `top bottom-=${bottom}px`,
    end: `bottom top+=${bottom}px`,
  });

  ScrollTrigger.batch('[data-inview]', {
    toggleClass: 'inview',
  });

  const ro = new ResizeObserver((entries) => {
    ScrollTrigger.refresh();
  });
  // 观察一个或多个元素
  ro.observe(document.querySelector('body'));
}

/**
 * 吸顶
 * @param {String} boxEl 容器css选择器
 * @param {String} El 吸顶元素css选择器
 */
export function sticky(boxEl, El, TOP = 0) {
  const box = $(boxEl);
  const el = $(El);

  if (box.length === 0) return;

  const boxH = box.outerHeight(true);
  const elH = el.outerHeight(true);
  const start = box.offset().top - TOP;
  const end = boxH - elH + start;

  box.css('position', 'relative');

  handleScroll({
    scroll(st) {
      if (st >= start) {
        el.css({
          position: 'fixed',
          top: `${TOP}px`,
        });
      } else {
        el.css('position', 'static');
      }

      if (st >= end) {
        el.css({
          position: 'absolute',
          top: `${end - start}px`,
        });
      }
    },
  });
}

export function inBox() {
  const box = document.querySelectorAll('.inbox');

  box.forEach((el) => {
    const text = el.textContent.split('\n');
    const dom = text.map((e) => {
      return `
          <div class="box"><div class="in">${e}</div></div>
        `.trim();
    });

    el.innerHTML = dom.join('');
  });
}

export function fadeOut(el) {
  el.style.opacity = 1;
  (function fade() {
    if ((el.style.opacity -= 0.03) < 0) {
      el.style.display = 'none';
    } else {
      requestAnimationFrame(fade);
    }
  })();
}

export function fadeIn(el, display) {
  el.style.opacity = 0;
  el.style.display = display || 'block';
  (function fade() {
    var val = parseFloat(el.style.opacity);
    if (!((val += 0.04) > 1)) {
      el.style.opacity = val;
      requestAnimationFrame(fade);
    }
  })();
}

/**
 * 点击目标非指定元素及其子元素时触发回调
 * @param {JqueryEl} node 指定元素
 * @param {Function} callback 回调
 */
export function awayClick(node, callback) {
  $(document).on('click', (e) => {
    const lg = node.parent().find(e.target).length;
    if (lg === 0) {
      callback &&
        (typeof callback == 'function'
          ? callback()
          : console.error('callback 必须是 function'));
    }
  });
}

/**
 * countUp
 * @param {string} className 需要countUp的元素类名 默认 .count
 */
export function countUp(className = '.count') {
  const els = document.querySelectorAll(className);
  els.forEach((el) => {
    const number = Number(el.textContent);
    new CountUp(el, number, {
      enableScrollSpy: true,
      separator: '',
    });
  });
}

/**
 * 文件下载
 * 查找a[download],重写下载逻辑,避免图片在新标签页打开
 */
export function resetDownload() {
  const body = document.querySelector('body');

  window.resetDownloadMethod = function (e) {
    e.preventDefault();
    const url = this.href;
    const split = url.split('/');
    const name = split[split.length - 1];

    const x = new XMLHttpRequest();
    x.open('GET', url, true);
    x.responseType = 'blob';
    x.onload = function (e) {
      const url = window.URL.createObjectURL(x.response);
      const a = document.createElement('a');
      a.href = url;
      a.download = decodeURI(name);
      a.click();
    };
    x.send();
  };

  const reset = () => {
    const files = document.querySelectorAll('a[download]');

    files.forEach((el) => {
      el.removeEventListener('click', window.resetDownloadMethod);
      el.addEventListener('click', window.resetDownloadMethod);
    });
  };

  reset();

  const observer = new MutationObserver(reset);
  observer.observe(body, {
    childList: true,
    subtree: true,
  });
}

/**
 * 控制元素显示隐藏
 * @param {String} el 被控元素 class
 */
export function changeDisplay(el) {
  if ($(el).css('display') == 'none') {
    $(el).fadeIn();
  } else {
    $(el).hide();
  }
}

/**
 * 复制链接
 */
export function copyLink() {
  const el = $('.copylink');

  el.click(() => {
    const link = window.location.href;
    const res = copyToClipboard(link);
    res
      .then(() => {
        alert('已成功将链接复制到剪切板');
      })
      .catch(() => {
        alert('链接复制失败, 请手动复制链接: ', link);
      });
  });
}

/**
 *  移动端横向菜单选中项定位
 */
export function scrollX() {
  const box = $('.nav, [sx], .crumbs');
  const active = box.find('.active,.on');
  const w = document.body.offsetWidth;

  if (w < 980) {
    if (box.length === 0) return;
    box.scrollLeft(active.position().left - box.position().left - w * 0.1);
  }
}

/**
 * @typedef swiperOptions
 * @prop {swiperOptions} mobile 移动端设置
 * @prop {{
 *  [key: number]: swiperOptions
 * }} breakpoints 断点
 * @prop {number | 'auto'} slidesPerView 展示个数
 * @prop {number} spaceBetween 间距
 * @prop {'horizontal'|'vertical'} 水平或垂直
 * @prop {'slide'|'fade'} effect
 * @prop {number} speed
 * @prop {boolean} loop
 * @prop {boolean | {
 *  disableOnInteraction: boolean
 * }} autoplay
 * @prop {number} delay
 * @prop {boolean} autovideoplay
 * @prop {boolean} observer
 * @prop {{
 *   nextEl: string
 *   prevEl: string
 * }} navigation
 * @prop {{
 *   init: (swiper) => void
 * }} on
 */

/**
 * 设置swiper
 * @param {String} boxEl swiper容器
 * @param {swiperOptions} opts 选项
 */
export function setSwiper(boxEl, opts = {}) {
  const box = document.querySelector(boxEl);
  if (box === null) return null;
  const { length } = [...box.children].filter((e) => {
    if (e.classList.contains('swiper-wrapper')) {
      return true;
    }
  });
  if (length == 0) return null;

  let options = {
    direction: 'horizontal',
    speed: 1500,
    loop: false,
    autoplay: false,
    delay: 4000,
    noSwiping: true,
    noSwipingClass: 'no-swiping',
    slideActiveClass: 'active',

    normalizeSlideIndex: false,
  };

  if (WINDTH < 980) {
    options = Object.assign(options, opts.mobile);
  }

  if (opts.effect === 'fade') {
    options = {
      ...options,
      fadeEffect: {
        crossFade: true,
      },
    };
  }

  if (opts.autoplay) {
    opts.autoplay = Object.assign(
      {
        delay: opts.delay || options.delay,
      },
      opts.autoplay
    );
  }

  if (opts.observer) {
    opts = {
      ...opts,
      observer: true,
      observeParents: true,
      observeSlideChildren: true,
    };
  }

  let final = Object.assign(options, opts);

  if (WINDTH < 980) {
    final = Object.assign(options, { ...opts, ...opts.mobile });
  }

  const swiper = new Swiper(boxEl, final);

  if (opts.autovideoplay) {
    let timer = null;
    function next() {
      swiperNext(swiper);
    }

    function videoslidechange() {
      const videos = swiper.el.querySelectorAll('video');
      const video = () =>
        swiper.slides[swiper.realIndex]?.querySelector('video');

      clearTimeout(timer);
      if (video()) {
        video().play();

        video().removeEventListener('ended', next);
        video().addEventListener('ended', next);
      } else {
        videos.forEach((e) => {
          e.pause();
          e.currentTime = 0;
        });

        timer = setTimeout(() => {
          swiperNext(swiper);
        }, options.delay || 4000);
      }
    }

    videoslidechange();
    swiper.on('slideChange', videoslidechange);
  }

  return swiper;
}

export function swiperNext(swiper) {
  const index = swiper.realIndex;

  if (index === swiper.slides.length - 1) {
    swiper.slideTo(0);
  } else {
    swiper.slideNext();
  }
}

export function swiperPrev(swiper) {
  const index = swiper.realIndex;

  if (index === 0) {
    swiper.slideTo(swiper.slides.length - 1);
  } else {
    swiper.slideTo(index - 1);
  }
}

/**
 * 禁止图片拖拽
 */
export function imgDraggable() {
  const imgs = $('img');
  imgs.attr('draggable', false).css('user-select', 'none');
}

/**
 * 监听大小变化
 * @param {css} el 需要监听的元素 css 选择器
 * @param {function} callback mutation =>
 */
export function onResizeObserver(el, callback) {
  const ro = new ResizeObserver((mutation) => {
    mutation.forEach((e) => callback(e));
  });
  ro.observe(document.querySelector(el));
}

/**
 * 监听dom的类名和样式变化
 * @param {Node} node 需要监听的节点
 * @param {function} callback mutation =>
 */
export function onAttrObserver(node, callback) {
  const AttrObserver = new MutationObserver((mutationsList) => {
    mutationsList.forEach(callback);
  });

  AttrObserver.observe(node, {
    attributes: true,
    attributeOldValue: true,
    attributeFilter: ['class', 'style'],
  });
}

/**
 * 处理弹窗
 * @param {ClassName} cls 元素类名
 * @returns {open, close}
 */
export function handlePop(cls) {
  const pop = $(cls);

  const close = () => pop.fadeOut();
  const open = () => pop.fadeIn();

  pop.click((e) => {
    const target = $(e.target);
    if (target.closest('.con').length === 0) {
      close();
    }
  });

  return { open, close };
}

/**
 * 处理滚动
 * @param {Object} funcs {scroll: func, up: func, down: func}
 */
export function handleScroll(funcs) {
  let position = $(window).scrollTop();

  $(window).on(
    'scroll',
    throttle(
      () => {
        const st = $(window).scrollTop();
        typeof funcs.scroll === 'function' && funcs.scroll(st);

        if (st > position) {
          typeof funcs.down === 'function' && funcs.down(st);
        } else {
          typeof funcs.up === 'function' && funcs.up(st);
        }
        position = st;
      },
      50,
      1
    )
  );
}

/**
 * 返回顶部
 * @param {cssSelect} $el 能通过css选择器查找到的点击元素 [to-top]
 */
export function toTop($el) {
  $el = $el || '[to-top]';
  const el = $($el);

  el.on('click', () => {
    $('html,body').stop().animate({ scrollTop: 0 }, 600);
  });

  const hide = () => el.fadeOut();

  let timer = null;
  const show = (flex = false) => {
    clearTimeout(timer);

    if (el.css('display') === 'none') {
      if (flex) {
        el.css('display', 'flex').hide().fadeIn();
      } else {
        el.fadeIn();
      }
    }

    timer = setTimeout(() => {
      hide();
    }, 2000);
  };

  return {
    show,
    hide,
    el,
  };
}

/**
 * 防抖
 * @param func 函数
 * @param wait 延迟执行毫秒数
 * @param immediate true 表立即执行，false 表非立即执行
 */
export function debounce(func, wait, immediate) {
  let timeout;
  return function () {
    let context = this;
    let args = arguments;

    if (timeout) clearTimeout(timeout);
    if (immediate) {
      let callNow = !timeout;
      timeout = setTimeout(() => {
        timeout = null;
      }, wait);

      if (callNow) func.apply(context, args);
    } else {
      timeout = setTimeout(() => {
        func.apply(context, args);
      }, wait);
    }
  };
}

/**
 * 节流
 * @param func 函数
 * @param wait 延迟执行毫秒数
 * @param type 1 表时间戳版，2 表定时器版
 */
export function throttle(func, wait, type) {
  let previous;
  if (type === 1) {
    previous = 0;
  } else if (type === 2) {
    let timeout;
  }
  return function () {
    let context = this;
    let args = arguments;
    if (type === 1) {
      let now = Date.now();
      if (now - previous > wait) {
        func.apply(context, args);
        previous = now;
      }
    } else if (type === 2) {
      if (!timeout) {
        timeout = setTimeout(() => {
          timeout = null;
          func.apply(context, args);
        }, wait);
      }
    }
  };
}
