import type { mastodon } from "masto";
import {
  type Component,
  For,
  Index,
  Match,
  Switch,
  createMemo,
  createRenderEffect,
  createSignal,
  onCleanup,
} from "solid-js";
import { css } from "solid-styled";
import tootStyle from "./toot.module.css";
import MediaViewer from "./MediaViewer";
import { render } from "solid-js/web";
import {
  createElementSize,
  useWindowSize,
} from "@solid-primitives/resize-observer";
import { useStore } from "@nanostores/solid";
import { $settings } from "../settings/stores";
import { averageColorHex } from "../platform/blurhash";

type ElementSize = { width: number; height: number };

function constraintedSize(
  { width: owidth, height: oheight }: Readonly<ElementSize>, // originalSize
  { width: mwidth, height: mheight }: Readonly<Partial<ElementSize>>, // modifier
  { width: maxWidth, height: maxHeight }: Readonly<ElementSize>, // maxSize
) {
  const ySize = owidth + (mwidth ?? 0);
  const yScale = ySize > maxWidth ? ySize / maxWidth : 1;
  const xSize = oheight + (mheight ?? 0);
  const xScale = xSize > maxHeight ? xSize / maxHeight : 1;

  const maxScale = Math.max(yScale, xScale);
  const scaledWidth = owidth / maxScale;
  const scaledHeight = oheight / maxScale;

  return {
    width: scaledWidth,
    height: scaledHeight,
  };
}

const MediaAttachmentGrid: Component<{
  attachments: mastodon.v1.MediaAttachment[];
}> = (props) => {
  const [rootRef, setRootRef] = createSignal<HTMLElement>();
  const [viewerIndex, setViewerIndex] = createSignal<number>();
  const viewerOpened = () => typeof viewerIndex() !== "undefined";
  const settings = useStore($settings);
  const windowSize = useWindowSize();

  createRenderEffect((lastDispose?: () => void) => {
    lastDispose?.();
    const vidx = viewerIndex();
    if (typeof vidx === "undefined") return;
    const container = document.createElement("div");
    container.setAttribute("role", "presentation");
    document.body.appendChild(container);
    return render(() => {
      onCleanup(() => {
        document.body.removeChild(container);
      });

      return (
        <MediaViewer
          show={viewerOpened()}
          index={viewerIndex() || 0}
          onIndexUpdated={setViewerIndex}
          media={props.attachments}
          onClose={() => setViewerIndex()}
        />
      );
    }, container);
  });

  const openViewerFor = (index: number) => {
    setViewerIndex(index);
  };

  const columnCount = () => {
    if (props.attachments.length === 1) {
      return 1;
    } else if (props.attachments.length % 2 === 0) {
      return 2;
    } else {
      return 3;
    }
  };

  const rawElementSize = createElementSize(rootRef);

  const elementWidth = () => rawElementSize.width;

  const itemMaxSize = createMemo(() => {
    const ewidth = elementWidth();
    const width = ewidth
      ? (ewidth - (columnCount() - 1) * 4) / columnCount()
      : 1;

    return {
      height: windowSize.height * 0.35,
      width,
    };
  });

  const itemStyle = (item: mastodon.v1.MediaAttachment) => {
    const { width, height } = constraintedSize(
      item.meta?.small || { width: 1, height: 1 },
      { width: 2, height: 2 },
      itemMaxSize(),
    );

    // I don't know why mastodon does not return this
    // and the condition for it to return this.
    // Anyway, It is useless now.
    // My hope is the FastAverageColor, but
    // we may need better tool to manage the performance impact
    // before using this. See #37.
    // TODO: use fast average color to extract accent color
    const accentColor =
      item.meta?.colors?.accent ??
      (item.blurhash ? averageColorHex(item.blurhash) : undefined);

    return Object.assign(
      {
        width: `${width}px`,
        height: `${height}px`,
        "contain-intrinsic-size": `${width}px ${height}px`,
      },
      accentColor ? { "--media-color-accent": accentColor } : {},
    );
  };

  css`
    .attachments {
      column-count: ${columnCount().toString()};
    }
  `;
  return (
    <section
      ref={setRootRef}
      class={[tootStyle.tootAttachmentGrp, "attachments"].join(" ")}
      onClick={(e) => {
        if (e.target !== e.currentTarget) {
          e.stopImmediatePropagation();
        }
      }}
    >
      <Index each={props.attachments}>
        {(item, index) => {
          const itemType = () => item().type;
          return (
            <Switch>
              <Match when={itemType() === "image"}>
                <img
                  data-sort={index}
                  data-media-type={item().type}
                  src={item().previewUrl}
                  width={item().meta?.small?.width}
                  height={item().meta?.small?.height}
                  alt={item().description || undefined}
                  onClick={[openViewerFor, index]}
                  loading="lazy"
                  style={itemStyle(item())}
                ></img>
              </Match>
              <Match when={itemType() === "video"}>
                <video
                  data-sort={index}
                  data-media-type={item().type}
                  src={item().url || undefined}
                  autoplay={settings().autoPlayVideos}
                  playsinline={settings().autoPlayVideos ? true : undefined}
                  controls
                  poster={item().previewUrl}
                  width={item().meta?.small?.width}
                  height={item().meta?.small?.height}
                  style={itemStyle(item())}
                />
              </Match>
              <Match when={itemType() === "gifv"}>
                <video
                  data-sort={index}
                  data-media-type={item().type}
                  src={item().url || undefined}
                  autoplay={settings().autoPlayGIFs}
                  controls
                  playsinline /* or safari on iOS will play in full-screen */
                  loop
                  poster={item().previewUrl}
                  width={item().meta?.small?.width}
                  height={item().meta?.small?.height}
                  style={itemStyle(item())}
                />
              </Match>
              <Match when={itemType() === "audio"}>
                <audio
                  data-sort={index}
                  data-media-type={item().type}
                  src={item().url || undefined}
                  controls
                ></audio>
              </Match>
            </Switch>
          );
        }}
      </Index>
    </section>
  );
};

export default MediaAttachmentGrid;
