import {
  Component,
  For,
  createSignal,
  ErrorBoundary,
  type Ref,
  createSelector,
} from "solid-js";
import { type mastodon } from "masto";
import { vibrate } from "../platform/hardware";
import Thread from "./Thread.jsx";
import { makeAcctText, useDefaultSession } from "../masto/clients";
import { useHeroSource } from "../platform/anim";
import { HERO as BOTTOM_SHEET_HERO } from "../material/BottomSheet";
import { setCache as setTootBottomSheetCache } from "./TootBottomSheet";
import { useNavigate } from "@solidjs/router";

/**
 * find bottom-to-top the element with `data-rel`.
 */
function findElementActionable(
  element: HTMLElement,
  top: HTMLElement,
): HTMLElement | undefined {
  let current = element;
  while (!current.dataset.rel) {
    if (!current.parentElement || current.parentElement === top) {
      return undefined;
    }
    current = current.parentElement;
  }
  return current;
}

const TootList: Component<{
  ref?: Ref<HTMLDivElement>;
  threads: string[];
  onUnknownThread: (id: string) => { value: mastodon.v1.Status }[] | undefined;
  onChangeToot: (id: string, value: mastodon.v1.Status) => void;
}> = (props) => {
  const session = useDefaultSession();
  const heroSrc = useHeroSource();
  const [expandedThreadId, setExpandedThreadId] = createSignal<string>();
  const navigate = useNavigate();

  const onBookmark = async (
    client: mastodon.rest.Client,
    status: mastodon.v1.Status,
  ) => {
    const result = await (status.bookmarked
      ? client.v1.statuses.$select(status.id).unbookmark()
      : client.v1.statuses.$select(status.id).bookmark());
    props.onChangeToot(result.id, result);
  };

  const onBoost = async (
    client: mastodon.rest.Client,
    status: mastodon.v1.Status,
  ) => {
    vibrate(50);
    const rootStatus = status.reblog ? status.reblog : status;
    const reblogged = rootStatus.reblogged;
    if (status.reblog) {
      status.reblog = { ...status.reblog, reblogged: !reblogged };
      props.onChangeToot(status.id, status);
    } else {
      props.onChangeToot(
        status.id,
        Object.assign(status, {
          reblogged: !reblogged,
        }),
      );
    }
    const result = reblogged
      ? await client.v1.statuses.$select(status.id).unreblog()
      : (await client.v1.statuses.$select(status.id).reblog()).reblog!;
    props.onChangeToot(
      status.id,
      Object.assign(status.reblog ?? status, result.reblog),
    );
  };

  const openFullScreenToot = (
    toot: mastodon.v1.Status,
    srcElement?: HTMLElement,
    reply?: boolean,
  ) => {
    const p = session()?.account;
    if (!p) return;
    const inf = p.inf;
    if (!inf) {
      console.warn("no account info?");
      return;
    }
    if (heroSrc) {
      heroSrc[1]((x) => ({ ...x, [BOTTOM_SHEET_HERO]: srcElement }));
    }

    const acct = `${inf.username}@${p.site}`;
    setTootBottomSheetCache(acct, toot);
    navigate(`/${encodeURIComponent(acct)}/toot/${toot.id}`, {
      state: reply
        ? {
            tootReply: true,
          }
        : undefined,
    });
  };

  const onItemClick = (
    status: mastodon.v1.Status,
    event: MouseEvent & { target: HTMLElement; currentTarget: HTMLElement },
  ) => {
    const actionableElement = findElementActionable(
      event.target,
      event.currentTarget,
    );

    if (actionableElement) {
      if (actionableElement.dataset.rel === "acct") {
        event.stopPropagation();

        const target = actionableElement as HTMLAnchorElement;

        const acct =
        encodeURIComponent(target.dataset.client ||
          `@${new URL(target.href).origin}`);

        navigate(`/${acct}/profile/${target.dataset.acctId}`);
      } else {
        console.warn("unknown action", actionableElement.dataset.rel);
      }
    } else {
      if (status.id !== expandedThreadId()) {
        setExpandedThreadId((x) => (x ? undefined : status.id));
      } else {
        openFullScreenToot(status, event.currentTarget as HTMLElement);
      }
    }
  };

  const checkIsExpendedId = createSelector(expandedThreadId);

  const checkIsExpended = (status: mastodon.v1.Status) =>
    checkIsExpendedId(status.id);

  const onReply = (
    { status }: { status: mastodon.v1.Status },
    element: HTMLElement,
  ) => {
    openFullScreenToot(status, element, true);
  };

  return (
    <ErrorBoundary
      fallback={(err, reset) => {
        return <p>Oops: {String(err)}</p>;
      }}
    >
      <div ref={props.ref} class="toot-list">
        <For each={props.threads}>
          {(itemId, index) => {
            const path = props.onUnknownThread(itemId)!;
            const toots = path.reverse().map((x) => x.value);

            return (
              <Thread
                toots={toots}
                onBoost={onBoost}
                onBookmark={onBookmark}
                onReply={onReply}
                client={session()?.client!}
                isExpended={checkIsExpended}
                onItemClick={onItemClick}
              />
            );
          }}
        </For>
      </div>
    </ErrorBoundary>
  );
};

export default TootList;
