/**
 * ページトランジションで必要な基本的な処理を [T_*フック](https://docs.not-equal.dev/reference/classes/Hook/#hook) に登録します。
 * Registers the basic necessary processes for page transitions in the [T_* hooks](https://docs.not-equal.dev/reference/classes/Hook/#hook).
 *
 * @param [Negl object](https://docs.not-equal.dev/reference/classes/Negl/)
 */
function registTransitionBase({ hook, scroller }) {
  /**
   * 注意）
   * この関数内で登録されるT_*フックに関しては第三引数の `type` を省略しているため、自動的に `base` が設定されます。`base` が設定された関数に関しては[トランジションタイプによらず共通で実行](https://docs.not-equal.dev/tutorial/transition/#トランジションのフックの実行-1)されます。
   *
   * その為、この処理は[トランジションタイプ](https://docs.not-equal.dev/tutorial/#spa-モードの概要)関係なく、ページトランジション時には必ず呼ばれる共通処理となります。
   *
   * Note:
   * For the T_* hooks registered within this function, the third argument `type` is omitted, so `base` is automatically set. Functions with `base` set are executed commonly [regardless of the transition type](https://docs.not-equal.dev/tutorial/transition/#トランジションのフックの実行-1).
   *
   * Therefore, this process becomes a common procedure that is always called during page transitions, [regardless of the transition type](https://docs.not-equal.dev/tutorial/#spa-モードの概要).
   */
  // トランジション開始時点
  // At the start of the transition
  hook.on(hook.T_BEGIN, disableMouse, { priority: 1000 });
  hook.on(hook.T_BEGIN, setStore, { priority: 900 });

  // トランジション 次のページの DOM 挿入前
  // Before inserting the DOM of the next page during the transition
  hook.on(hook.T_NEXT_DOM_BEFORE, swapBodyClasses, { priority: 2000 });
  hook.on(hook.T_NEXT_DOM_BEFORE, fixCurrentPage, { priority: 1000 });
  hook.on(hook.T_NEXT_DOM_BEFORE, transparentNextHTML, { priority: 900 });

  // トランジション 次のページのDOM挿入後
  // After inserting the DOM of the next page during the transition
  hook.on(hook.T_BOTH_DOM_EXIST, disableScroll, { priority: 1000 });
  hook.on(hook.T_BOTH_DOM_EXIST, scrollToPos, { priority: 900 });

  // トランジション 次のページのOBインスタンス挿入後
  // After inserting the OB instance of the next page during the transition
  hook.on(hook.T_BOTH_OB_EXIST, runPageDefault, { priority: 1000 });

  // トランジション 前ページのDOM、OBインスタンス消滅後
  // After the DOM and OB instance of the previous page have disappeared during the transition
  hook.on(hook.T_END, updateHead);
  hook.on(hook.T_END, resetCurrentPageInlineStyle);
  hook.on(hook.T_END, enableAllInteraction, { priority: -100 });

  /**
   * ボディーのクラスを付け替える
   * Swap the classes of the body element
   * * 必要な場合、T_NEXT_DOM_BEFORE フックに登録して使用してください。
   * * If necessary, register and use it in the T_NEXT_DOM_BEFORE hook.
   *
   * @param [TransitionState](https://docs.not-equal.dev/reference/interfaces/TransitionState/)
   */
  function swapBodyClasses({ next }) {
    // 次のページのbodyについているクラスを全て取得
    // Get all the classes attached to the body of the next page
    const nextBodyClassSet = new Set(next.doc.body.classList);
    // 現在のページのbodyについているクラスを全て取得
    // Get all the classes attached to the body of the current page
    const currentBodyClasses = Array.from(document.body.classList);
    // 次ページには存在しない現在のページのbodyについているクラスを取得
    // Get the classes attached to the body of the current page that do not exist on the next page
    const onlyCurrentClasses = currentBodyClasses.filter(
      (cls) => !nextBodyClassSet.has(cls)
    );

    // bodyに新しいページのbodyクラスを追加
    // Add the body classes of the new page to the body
    document.body.classList.add(...nextBodyClassSet, "transitioning");

    // トランジション終了時に前ページのbodyクラスを削除
    // Remove the body classes of the previous page at the end of the transition
    hook.on(
      hook.T_END,
      () => {
        document.body.classList.remove(...onlyCurrentClasses, "transitioning");
      },
      { once: true }
    );
  }

  /**
   * 古いページを position: fixed にし、新しいページを重ねて表示できるようにする
   * Make the old page position: fixed to overlay the new page for display
   * @param [TransitionState](https://docs.not-equal.dev/reference/interfaces/TransitionState/)
   */
  async function fixCurrentPage({ current: { el } }) {
    const currentRect = el.getBoundingClientRect();
    el.style.top = currentRect.y + "px";
    el.style.width = `100%`;
    el.style.position = "fixed";
    el.style.left = 0;
  }

  /**
   * 次のページの .page-container の透明度を 0 にする。
   * Set the opacity of the .page-container of the next page to 0.
   * @param [TransitionState](https://docs.not-equal.dev/reference/interfaces/TransitionState/)
   */
  function transparentNextHTML({ next: { el } }) {
    el.style.opacity = 0;
  }

  /**
   * 現在ページのスクロール位置を URL 単位で保持する
   * Maintain the scroll position of the current page on a per-URL basis
   * @param [TransitionState](https://docs.not-equal.dev/reference/interfaces/TransitionState/)
   * @param [TransitionState](https://docs.not-equal.dev/reference/interfaces/TransitionState/)
   */
  function setStore({ current: { store } }) {
    // 画面を離れた時のスクロールのポジションを保持
    // Maintain the scroll position when leaving the screen
    // scroller.scrollTop - https://docs.not-equal.dev/reference/classes/Scroller/#scrolltop
    // current.store - https://docs.not-equal.dev/reference/interfaces/TransitionState/#current
    store.set("scrollTop", scroller.scrollTop);
  }

  /**
   * 一度訪問したページでは画面遷移時のスクロール位置までスクロールさせる
   * Scroll to the scroll position at the time of page transition on pages that have been visited once
   * @param [TransitionState](https://docs.not-equal.dev/reference/interfaces/TransitionState/)
   */
  async function scrollToPos({ next: { store } }) {
    // 特定のポジションまでスクロール
    // Scroll to a specific position
    const scrollTop = store.get("scrollTop") ?? 0;
    // scroller.scrollTop - https://docs.not-equal.dev/reference/classes/Scroller/#scrolltop
    scroller.scrollTop = scrollTop;
  }

  /**
   * トランジション中はマウス操作を効かなくする
   * Disable mouse interaction during the transition
   */
  function disableMouse() {
    document.body.style.pointerEvents = "none";
  }

  /**
   * トランジション中はスクロール操作を効かなくする
   * Disable scroll interaction during the transition
   */
  function disableScroll() {
    // scroller.disable - https://docs.not-equal.dev/reference/classes/Scroller/#disable
    scroller.disable();
  }

  /**
   * トランジション終了時に操作可能な状態に戻す
   * Restore to an operable state at the end of the transition
   * @param [TransitionState](https://docs.not-equal.dev/reference/interfaces/TransitionState/)
   */
  function enableAllInteraction() {
    document.body.style.pointerEvents = "";
    // scroller.disable - https://docs.not-equal.dev/reference/classes/Scroller/#disable
    // scroller.enable - https://docs.not-equal.dev/reference/classes/Scroller/#enable
    scroller.enable();
  }

  /**
   * ページモジュールの default エクスポートした関数を実行する。
   * Execute the default exported function of the page module.
   */
  async function runPageDefault(state) {
    await state.next.page?.default?.(state);
  }

  /**
   * head 内のタグを更新する
   * Update the tags inside the head element
   * @param [TransitionState](https://docs.not-equal.dev/reference/interfaces/TransitionState/)
   */
  function updateHead({ next }) {
    // タイトルの置き換え
    // Replace the title
    document.title = next.head.querySelector("title").textContent;

    // メタタグの置き換え
    // Replace the meta tags
    const targetMetaSelectors = [
      'meta[name="description"]',
      'meta[property="og:title"]',
      'meta[property="og:description"]',
      'meta[property="og:url"]',
      'meta[property="og:site_name"]',
    ];

    targetMetaSelectors.forEach((selector) => {
      const currentDescriptionEl = document.querySelector(selector);
      const nextDescriptionEl = next.head.querySelector(selector);
      currentDescriptionEl?.before?.(nextDescriptionEl);
      currentDescriptionEl?.remove?.();
    });
  }

  /**
   * 現在のページのスタイルをもとに戻す
   * Reset the style of the current page to its original state
   * @param [TransitionState](https://docs.not-equal.dev/reference/interfaces/TransitionState/)
   */
  async function resetCurrentPageInlineStyle({ current: { el } }) {
    el.style.top = null;
    el.style.opacity = null;
    el.style.position = null;
  }
}
export { registTransitionBase };
