import clsx from "clsx";
import {
  MouseEvent as ReactMouseEvent,
  Suspense,
  useEffect,
  useRef,
  useState,
} from "react";
import { useNavigate, Await } from "react-router-dom";
import MicroModal from "micromodal";
import { ICONS_PREPEND, THUMBNAILS_PREPEND, VIDEO_AD } from "#env/env-vars";
import {
  createAttachmentURL,
  createBannerURL,
  createIconURL,
  createPostRevisionPageURL,
  createPostURL,
  createPreviewURL,
  createProfileTagURL,
  createProfilePageURL,
} from "#lib/urls";
import { fetchPost, flagPost } from "#api/posts";
import { MiddleAd } from "#components/ads";
import { Image, ImageBackground, ImageLink } from "#components/images";
import { KemonoLink, LocalLink } from "#components/links";
import { Timestamp } from "#components/dates";
import { LoadingIcon } from "#components/loading";
import { IPaySite } from "#entities/paysites";
import {
  IComment,
  IPost,
  IPostAttachment,
  IPostPoll,
  IPostPreview,
  IPostVideo,
  IPreviewEmbed,
  IPreviewThumbnail,
} from "#entities/posts";
import { IArtistDetails } from "#entities/profiles";
import {
  addPostToFavourites,
  isFavouritePost,
  isRegisteredAccount,
  removePostFromFavourites,
} from "#entities/account";

import "fluid-player/src/css/fluidplayer.css";
import styles from "./overview.module.scss";

interface IPostOverviewProps {
  post: IPost;

  profile: IArtistDetails;
  revisions: Awaited<ReturnType<typeof fetchPost>>["props"]["revisions"];

  /**
   * TODO: wtf
   */
  flagged?: 0;
  videos?: IPostVideo[];
  attachments?: IPostAttachment[];
  previews?: IPostPreview[];
  archives_enabled?: boolean;
  comments: Promise<IComment[]>;
  postTitle: string;
  paysite: IPaySite;
}

interface IPostHeaderProps
  extends Pick<
    IPostOverviewProps,
    "post" | "profile" | "revisions" | "flagged"
  > {
  postTitle: string;
  paysite: IPaySite;
}

export function PostOverview({
  post,
  profile,
  revisions,
  flagged,
  videos,
  attachments,
  previews,
  archives_enabled,
  comments,
  postTitle,
  paysite,
}: IPostOverviewProps) {
  useEffect(() => {
    document.addEventListener("DOMContentLoaded", handleShowTagsButton);
    window.addEventListener("resize", handleShowTagsButton);

    return () => {
      document.removeEventListener("DOMContentLoaded", handleShowTagsButton);
      window.removeEventListener("resize", handleShowTagsButton);
    };
  }, []);

  function handleShowTagsButton() {
    addShowTagsButton();
  }

  return (
    <>
      <PostHeader
        post={post}
        profile={profile}
        postTitle={postTitle}
        paysite={paysite}
        revisions={revisions}
        flagged={flagged}
      />

      <PostBody
        post={post}
        videos={videos}
        attachments={attachments}
        previews={previews}
        archives_enabled={archives_enabled}
      />

      <PostFooter
        comments={comments}
        service={post.service}
        profileID={post.user}
        profileName={profile.name}
      />
    </>
  );
}

function PostHeader({
  post,
  profile,
  postTitle,
  paysite,
  revisions,
  flagged,
}: IPostHeaderProps) {
  const navigate = useNavigate();
  const profileURL = String(
    createProfilePageURL({ service: post.service, profileID: post.user })
  );
  const profileIcon = createIconURL(post.service, post.user);
  const bannerURL = createBannerURL(post.service, post.user);

  return (
    <header className="post__header">
      <div className="post__user">
        <ImageBackground src={bannerURL} />
        <ImageLink
          url={profileURL}
          src={profileIcon}
          isLazy={false}
          className="post__user-profile"
        />

        <div>
          <KemonoLink className="post__user-name" url={profileURL}>
            {!profile ? `Profile page` : profile.name}
          </KemonoLink>
        </div>
      </div>

      <div className="post__info">
        <h1 className="post__title">
          <span>{postTitle}</span> <span>({paysite.title})</span>
        </h1>

        {!post.published ? undefined : (
          <div className="post__published" style={{ margin: "0.125rem 0" }}>
            <div style={{ width: "89px", display: "inline-block" }}>
              Published:
            </div>{" "}
            {post.published.replace("00:00:00", "")}
          </div>
        )}

        {!post.edited || post.edited === post.published ? undefined : (
          <div className="post__edited" style={{ margin: "0.125rem 0" }}>
            <div style={{ display: "inline-block", width: "89px" }}>
              Edited:
            </div>{" "}
            {post.edited.replace("00:00:00", "")}
          </div>
        )}

        <div className="post__added" style={{ margin: "0.125rem 0" }}>
          {revisions.length < 2 ? (
            <span>
              <div style={{ width: "89px", display: "inline-block" }}>
                Imported:{" "}
              </div>{" "}
              {post.added.slice(0, 7)}
            </span>
          ) : (
            <>
              <div
                style={{
                  display: "inline-block",
                  width: "85px",
                  margin: "0",
                }}
              >
                Imported:
              </div>
              <span
                className="post__revisions"
                title="View past versions of this page"
              >
                <select
                  id="post-revision-selection"
                  style={{ textAlignLast: "left" }}
                  defaultValue={post.revision_id}
                  onChange={(event) => {
                    const revisionID = event.currentTarget.value;

                    if (revisionID) {
                      navigate(
                        String(
                          createPostRevisionPageURL(
                            post.service,
                            post.user,
                            post.id,
                            revisionID
                          )
                        )
                      );
                    } else {
                      navigate(
                        String(createPostURL(post.service, post.user, post.id))
                      );
                    }
                  }}
                >
                  {revisions.map(([index, postRevision]) => (
                    <option key={index} value={postRevision.revision_id ?? ""}>
                      {postRevision.added?.slice(0, 7)} {"<"}
                      {index}
                      {">"} [{postRevision.revision_id ?? "current"}]
                    </option>
                  ))}
                </select>
              </span>
            </>
          )}
        </div>

        {post.tags && post.tags.length !== 0 && (
          <section id="post-tags">
            <span id="label">Tags: </span>
            <div>
              {post.tags.map((tag, index) => (
                <a
                  key={index}
                  href={String(
                    createProfileTagURL(post.service, post.user, tag)
                  )}
                >
                  {tag}
                </a>
              ))}
            </div>
          </section>
        )}

        <PostActions
          service={post.service}
          profileID={post.user}
          postID={post.id}
          flagged={flagged}
        />
      </div>
    </header>
  );
}

interface IPostBodyProps
  extends Pick<
    IPostOverviewProps,
    "post" | "videos" | "attachments" | "previews" | "archives_enabled"
  > {}

const formattedServices = ["fantia", "onlyfans", "fansly", "candfans"];

function PostBody({
  post,
  attachments,
  previews,
  videos,
  archives_enabled,
}: IPostBodyProps) {
  const { incomplete_rewards, poll, content, service, user, id } = post;
  const postBodyRef = useRef<HTMLDivElement>(null);

  useEffect(
    () => {
      const bodyElement = postBodyRef.current;

      if (!bodyElement) {
        return;
      }

      cleanupBody(bodyElement, service);
    },
    // rerun it on post change
    [id, user, service]
  );

  return (
    <div ref={postBodyRef} className="post__body">
      <MiddleAd />

      {post.service === "dlsite" && attachments && attachments.length > 1 && (
        <p className="subtitle">
          This DLsite post was received as a split set of multiple files due to
          file size. Download all the files, then open the .exe file to compile
          them into a single one.
        </p>
      )}

      {videos && videos.length !== 0 && (
        <>
          <h2>Videos</h2>
          <script
            // yikes
            dangerouslySetInnerHTML={{
              __html: `window.videoAds = ${VIDEO_AD}`,
            }}
          ></script>
          <ul style={{ textAlign: "center", listStyleType: "none" }}>
            {videos.map((video, index) => (
              <PostVideo key={index} video={video} />
            ))}
          </ul>
        </>
      )}

      {attachments && attachments.length !== 0 && (
        <>
          <h2>Downloads</h2>
          <ul className="post__attachments">
            {attachments.map((attachment, index) => (
              <PostAttachment
                key={index}
                attachment={attachment}
                archives_enabled={archives_enabled}
              />
            ))}
          </ul>
        </>
      )}

      {incomplete_rewards && (
        <div className="post__warning" style={{ color: "red" }}>
          <pre dangerouslySetInnerHTML={{ __html: incomplete_rewards }}></pre>
        </div>
      )}

      {poll && <PostPoll poll={poll} />}

      {content && (
        <>
          <h2>Content</h2>
          {/* TODO: rewrite without this */}
          <div className="post__content">
            <pre dangerouslySetInnerHTML={{ __html: content }}></pre>
          </div>
        </>
      )}

      {previews && <PostPreviews previews={previews} />}
    </div>
  );
}

/**
 * Apply some fixes to the content of the post.
 */
function cleanupBody(postBody: HTMLElement, service: string) {
  const postContent = postBody.querySelector(".post__content");
  const isNoPostContent =
    !postContent ||
    (!postContent.childElementCount && !postContent.childNodes.length);

  // content is empty
  if (isNoPostContent) {
    return;
  }

  // pixiv post
  if (service === "fanbox") {
    // its contents is a text node
    if (!postContent.childElementCount && postContent.childNodes.length === 1) {
      // wrap the text node into `<pre>`
      const [textNode] = Array.from(postContent.childNodes);
      const pre = document.createElement("pre");
      textNode.after(pre);
      pre.appendChild(textNode);
    }

    // remove paragraphs with only `<br>` in them
    const paragraphs = postContent.querySelectorAll("p");
    paragraphs.forEach((para) => {
      if (
        para.childElementCount === 1 &&
        para.firstElementChild?.tagName === "BR"
      ) {
        para.remove();
      }
    });
  }

  Array.from(document.links).forEach((anchour) => {
    // remove links to fanbox from the post
    const hostname = anchour.hostname;
    if (hostname.includes("downloads.fanbox.cc")) {
      if (anchour.classList.contains("image-link")) {
        anchour.remove();
      } else {
        let el = document.createElement("span");
        el.textContent = anchour.textContent;
        anchour.replaceWith(el);
      }
    } else if (hostname.includes("fanbox.cc")) {
      anchour.href = anchour.href.replace(
        /https?:\/\/(?:[a-zA-Z0-9-]*.)?fanbox\.cc\/(?:(?:manage\/)|(?:@[a-zA-Z\d]+\/)|)posts\/(\d+)/g,
        "/fanbox/post/$1"
      );
    } else if (hostname.includes("patreon.com")) {
      anchour.href = anchour.href.replace(
        /https?:\/\/(?:[\w-]*.)?patreon\.com\/posts\/.*\b(\d+)\b(?:\?.*)?/g,
        "/patreon/post/$1"
      );
    }
  });

  // Remove needless spaces and empty paragraphs.
  /**
   * @type {NodeListOf<HTMLParagraphElement}
   */
  const paragraphs = postContent.querySelectorAll("p:empty");
  Array.from(paragraphs).forEach((paragraph) => {
    if (
      paragraph.nextElementSibling &&
      paragraph.nextElementSibling.tagName === "BR"
    ) {
      paragraph.nextElementSibling.remove();
      paragraph.remove();
    } else {
      paragraph.remove();
    }
  });
}

interface IPostVideoProps {
  video: IPostVideo;
}

function PostVideo({ video }: IPostVideoProps) {
  return (
    <li>
      <summary>{video.name}</summary>

      {!video.caption ? undefined : <summary>{video.caption}</summary>}

      <video
        id={`kemono-player${video.index}`}
        className="post__video"
        controls
        playsInline
        preload="none"
      >
        <source
          src={`${video.server ?? ""}/data${video.path}`}
          type={`video/${video.extension.slice(1)}`}
        />
      </video>
    </li>
  );
}

interface IPostAttachmentProps
  extends Pick<IPostOverviewProps, "archives_enabled"> {
  attachment: IPostAttachment;
}

const archiveFileExtension = [".zip", ".rar", ".7z"];

function PostAttachment({
  attachment,
  archives_enabled,
}: IPostAttachmentProps) {
  const { name, path, server, extension, name_extension, stem } = attachment;
  const isArchiveFile = Boolean(
    archives_enabled &&
      (archiveFileExtension.includes(extension) ||
        archiveFileExtension.includes(name_extension))
  );

  return (
    <li className="post__attachment">
      <a
        className="post__attachment-link"
        href={String(createAttachmentURL(path, name, server))}
        download={name}
      >
        Download {name}
      </a>
      {isArchiveFile && (
        <>
          {/* TODO: a proper URL function */}(
          <a href={`/posts/archives/${stem}`}>browse »</a>)
        </>
      )}
    </li>
  );
}

interface IPostPollProps {
  poll: IPostPoll;
}

function PostPoll({ poll }: IPostPollProps) {
  const {
    title,
    description,
    choices,
    total_votes,
    created_at,
    closes_at,
    allow_multiple,
  } = poll;

  return (
    <>
      <h2>Poll</h2>

      <article id="poll">
        <div id="poll-summary">
          <h3>{title}</h3>
          {!description ? undefined : <h4>{description}</h4>}
        </div>

        <ul>
          {choices.map((choice) => {
            const percentage = (choice.votes / (total_votes ?? 1)) * 100;

            return (
              <li title={`${percentage}%`}>
                <span className="choice-text">{choice.text}</span>
                <span className="choice-votes">{choice.votes}</span>
                <span
                  className="choice-fill"
                  style={{ width: `${percentage}%` }}
                ></span>
              </li>
            );
          })}
        </ul>

        <footer>
          <ul>
            <li id="start" title={created_at}>
              {created_at}
            </li>
            {closes_at && (
              <li id="end" title={closes_at}>
                —{closes_at}
              </li>
            )}

            {allow_multiple && (
              <>
                <span className="sep"></span>
                <li id="multiple-choice">multiple choice</li>
              </>
            )}

            <span className="sep"></span>
            <li id="vote-count">{total_votes} votes</li>
          </ul>
        </footer>
      </article>
      <span style={{ display: "none" }}>{String(poll)}</span>
    </>
  );
}

interface IPostPreviewsProps
  extends Pick<Required<IPostOverviewProps>, "previews"> {}

function PostPreviews({ previews }: IPostPreviewsProps) {
  return (
    <>
      <h2>Files</h2>
      <div className="post__files">
        {previews.map((preview, index) =>
          preview.type === "thumbnail" ? (
            <PreviewThumbnail
              key={`${index}-${preview.name}`}
              preview={preview}
            />
          ) : (
            <PreviewEmbed key={`${index}-${preview.url}`} preview={preview} />
          )
        )}
      </div>
    </>
  );
}

interface IPreviewThumbnailProps {
  preview: IPreviewThumbnail;
}

function PreviewThumbnail({ preview }: IPreviewThumbnailProps) {
  const [isExpanded, switchExpansion] = useState(false);
  const { server, path, name, caption } = preview;
  const url = String(createPreviewURL(path, name, server));
  const downloadName = encodeURIComponent(name);
  const thumbnailRef = useRef<HTMLDivElement>(null);

  return (
    <div ref={thumbnailRef} className="post__thumbnail">
      <figure>
        {!isExpanded ? (
          <a
            className="fileThumb image-link"
            href={url}
            download={downloadName}
            // this is gigatrash
            onClick={(event) => {
              const thumbnailElement = thumbnailRef.current;

              if (!thumbnailElement) {
                return;
              }

              event.preventDefault();
              setTimeout(() => {
                switchExpansion(true);
                thumbnailElement.scrollIntoView({
                  behavior: "smooth",
                  block: "start",
                });
              }, 10);
            }}
          >
            {/* TODO: move backup image logic to the script */}
            <img
              data-src={`${THUMBNAILS_PREPEND}/thumbnail/data${path}`}
              src={`${THUMBNAILS_PREPEND}/thumbnail/data${path}`}
              loading="lazy"
            />
          </a>
        ) : (
          <div className={styles.expanded}>
            <img src={url} alt="" />
          </div>
        )}

        {caption && <figcaption>{caption}</figcaption>}
      </figure>
    </div>
  );
}

interface IPreviewEmbedProps {
  preview: IPreviewEmbed;
}

function PreviewEmbed({ preview }: IPreviewEmbedProps) {
  const { url, description, subject } = preview;

  return (
    <a href={url} target="_blank">
      <div className="embed-view">
        <h3 className={subject ? undefined : "subtitle"}>
          {!subject ? "(No title)" : subject}
        </h3>
        {description && <p>{description}</p>}
      </div>
    </a>
  );
}

interface IPostFooterProps extends Pick<IPostOverviewProps, "comments"> {
  service: string;
  profileID: string;
  profileName?: string;
}

function PostFooter({
  comments,
  service,
  profileID,
  profileName,
}: IPostFooterProps) {
  return (
    <footer className="post__footer">
      <h2 className="site-section__subheading">Comments</h2>
      {/* TODO: comment filters */}
      <Suspense fallback={<p>Loading comments...</p>}>
        <Await resolve={comments} errorElement={<></>}>
          {(comments: IComment[]) => (
            <div
              className={clsx(
                "post__comments",
                !comments && "post__comments--no-comments"
              )}
            >
              {!comments ? (
                <p className="subtitle">No comments found for this post.</p>
              ) : (
                comments.map((comment) => (
                  <PostComment
                    comment={comment}
                    service={service}
                    postProfileID={profileID}
                    postProfileName={profileName}
                  />
                ))
              )}
            </div>
          )}
        </Await>
      </Suspense>
    </footer>
  );
}

interface IPostCommentProps {
  comment: IComment;
  postProfileID: string;
  postProfileName?: string;
  service: string;
}

function PostComment({
  comment,
  postProfileID,
  postProfileName,
  service,
}: IPostCommentProps) {
  const {
    id,
    commenter,
    commenter_name,
    revisions,
    parent_id,
    content,
    published,
  } = comment;
  const isProfileComment = commenter === postProfileID;
  const modalID = `comment-revisions-${id}`;

  return (
    <article
      className={clsx("comment", isProfileComment && "comment--user")}
      id={id}
    >
      <header className="comment__header">
        {!isProfileComment ? (
          <LocalLink id={id} className="comment__name">
            {commenter_name ?? "Anonymous"}
          </LocalLink>
        ) : (
          <>
            {/* TODO: a proper local link */}
            <KemonoLink url={id} className="comment__icon">
              <Image
                src={`${ICONS_PREPEND}/icons/${service}/${postProfileID}`}
              />
            </KemonoLink>
            <LocalLink id={id} className="comment__name">
              {postProfileName ?? "Post's profile"}
            </LocalLink>
          </>
        )}

        {revisions && revisions.length !== 0 && (
          <>
            <span
              className="comment-has-revisions fancy-link"
              data-micromodal-trigger={modalID}
              onClick={() => MicroModal.show(modalID)}
            >
              (
              <a className="fancy-link" href={`#${id}`}>
                edited
              </a>
              )
            </span>
            <div className="comment-revisions-dialog" id={modalID}>
              <div tabIndex={-1} data-micromodal-close>
                <div
                  role="dialog"
                  aria-modal="true"
                  aria-labelledby={`comment-revisions-${id}-title`}
                >
                  <section>
                    <header>
                      <h3 id={`comment-revisions-${id}-title`}>
                        Comment edits
                      </h3>

                      <button
                        data-micromodal-close
                        onClick={() => MicroModal.close(modalID)}
                      ></button>
                    </header>

                    <main id={`comment-revisions-${id}-content`}>
                      {[...revisions, comment].map((revision) => (
                        <article
                          id={`comment-${comment.id}-revision-${revision.id}`}
                        >
                          <span className="timestamp">
                            {revision.published ?? revision.added}
                          </span>
                          <span className="prose">{revision.content}</span>
                        </article>
                      ))}
                    </main>
                  </section>
                </div>
              </div>
            </div>
          </>
        )}
      </header>

      <section className="comment__body">
        {parent_id && (
          <div className="comment__reply">
            <a href={`#${parent_id}`}>
              {">>"}
              {parent_id}
            </a>
          </div>
        )}
        <pre>
          <p className="comment__message">
            {service !== "boosty" ? (
              content
            ) : (
              <pre>
                {content.replace(
                  '/size/large" title="',
                  '/size/small" title="'
                )}
              </pre>
            )}
          </p>
        </pre>
      </section>
      <footer className="comment__footer">
        <Timestamp time={published} />
      </footer>
    </article>
  );
}

interface IPostActionsProps extends Pick<IPostOverviewProps, "flagged"> {
  service: string;
  profileID: string;
  postID: string;
}

function PostActions({
  service,
  profileID,
  postID,
  flagged,
}: IPostActionsProps) {
  return (
    <div className="post__actions">
      <FlagButton
        service={service}
        profileID={profileID}
        postID={postID}
        flagged={flagged}
      />

      <FavoriteButton service={service} profileID={profileID} postID={postID} />
    </div>
  );
}

interface IFlagButtonProps
  extends Pick<
    IPostActionsProps,
    "flagged" | "service" | "profileID" | "postID"
  > {}

/**
 * TODO: promptless flagging
 */
function FlagButton({ service, profileID, postID, flagged }: IFlagButtonProps) {
  const [isFlagged, switchFlag] = useState(Boolean(flagged));
  const [isFlagging, switchFlagging] = useState(false);
  const renderKey = `${postID}${profileID}${service}`;

  async function handleFlagging() {
    const isConfirmed = confirm(
      "Are you sure you want to flag this post for reimport? Only do this if data in the post is broken/corrupted/incomplete.\nThis is not a deletion button."
    );

    if (!isConfirmed) {
      return;
    }

    try {
      switchFlagging(true);

      await flagPost(service, profileID, postID);

      switchFlag(true);

    } finally {
      switchFlagging(false);
    }
  }

  return !isFlagged ? (
    <button
      key={renderKey}
      className={clsx("post__flag", isFlagging && "post__flag--loading")}
      type="button"
      disabled={isFlagging}
      onClick={handleFlagging}
    >
      <span className="post__flag-icon">
        {isFlagging ? <LoadingIcon /> : <>⚑</>}
      </span>
      <span>Flag</span>
    </button>
  ) : (
    <span key={renderKey} className={clsx("post__flag", "post__flag--flagged")}>
      <span className="post__flag-icon">⚑</span>
      <span>Flagged</span>
    </span>
  );
}

interface IFavoriteButtonProps {
  service: string;
  profileID: string;
  postID: string;
}

function FavoriteButton({ service, profileID, postID }: IFavoriteButtonProps) {
  const [isFavorite, switchFavorite] = useState(false);
  const [isLoading, switchLoading] = useState(true);
  const renderKey = `${postID}${profileID}${service}`;

  useEffect(() => {
    (async () => {
      try {
        switchLoading(true);

        const isLoggedIn = await isRegisteredAccount();

        if (!isLoggedIn) {
          return;
        }

        const isFav = await isFavouritePost(service, profileID, postID);

        switchFavorite(isFav);
      } catch (error) {
        // TODO: better error handling
        console.error(error);
      } finally {
        switchLoading(false);
      }
    })();
  }, [service, profileID, postID]);

  async function handleFavorite() {
    try {
      switchLoading(true);
      await addPostToFavourites(service, profileID, postID);
      switchFavorite(true);
    } catch (error) {
      // TODO: better error handling
      console.error(error);
    } finally {
      switchLoading(false);
    }
  }
  async function handleUnfavorite() {
    try {
      switchLoading(true);
      await removePostFromFavourites(service, profileID, postID);
      switchFavorite(false);
    } catch (error) {
      // TODO: better error handling
      console.error(error);
    } finally {
      switchLoading(false);
    }
  }

  return isFavorite ? (
    <button
      key={renderKey}
      className={clsx("post__fav", "post__fav--unfav")}
      type="button"
      disabled={isLoading}
      onClick={handleUnfavorite}
    >
      <span className="post__fav-icon">★</span>
      <span>Unfavorite</span>
    </button>
  ) : (
    <button
      key={renderKey}
      className={clsx("post__fav")}
      type="button"
      disabled={isLoading}
      onClick={handleFavorite}
    >
      <span className="post__fav-icon">☆</span>
      <span>Favorite</span>
    </button>
  );
}

function addShowTagsButton() {
  let div = document.querySelector("#post-tags > div");

  if (document.getElementById("show-tag-overflow-button")) {
    // @ts-expect-error no fucking idea what it does
    document.getElementById("show-tag-overflow-button").remove();
  }
  // @ts-expect-error no fucking idea what it does

  if (div && div.offsetWidth < div.scrollWidth) {
    // tags overflow
    let button = document.createElement("a");
    button.href = "javascript:void 0";
    button.id = "show-tag-overflow-button";
    button.textContent = "Show all »";
    button.onclick = (e) => {
      if (div.classList.contains("show-overflow")) {
        div.classList.remove("show-overflow");
        button.textContent = "Show all»";
      } else {
        div.classList.add("show-overflow");
        button.textContent = "« Hide";
      }
    };
    // @ts-expect-error no fucking idea what it does
    div.parentElement.appendChild(button);
  }
}
