import { useLocation, useNavigate, useParams } from "@solidjs/router";
import {
  createEffect,
  createRenderEffect,
  createResource,
  createSignal,
  For,
  Show,
  type Component,
} from "solid-js";
import Scaffold from "../material/Scaffold";
import { AppBar, CircularProgress, IconButton, Toolbar } from "@suid/material";
import { Title } from "../material/typography";
import {
  ArrowBack as BackIcon,
  Close as CloseIcon,
} from "@suid/icons-material";
import { useSessionForAcctStr } from "../masto/clients";
import { resolveCustomEmoji } from "../masto/toot";
import RegularToot from "./RegularToot";
import type { mastodon } from "masto";
import cards from "../material/cards.module.css";
import { css } from "solid-styled";
import { vibrate } from "../platform/hardware";
import { createTimeSource, TimeSourceProvider } from "../platform/timesrc";
import TootComposer from "./TootComposer";
import { useDocumentTitle } from "../utils";
import { createTimelineControlsForArray } from "../masto/timelines";
import TootList from "./TootList";

let cachedEntry: [string, mastodon.v1.Status] | undefined;

export function setCache(acct: string, status: mastodon.v1.Status) {
  cachedEntry = [acct, status];
}

function getCache(acct: string, id: string) {
  if (acct === cachedEntry?.[0] && id === cachedEntry?.[1].id) {
    return cachedEntry[1];
  }
}

const TootBottomSheet: Component = (props) => {
  const params = useParams<{ acct: string; id: string }>();
  const location = useLocation<{
    tootBottomSheetPushedCount?: number;
    tootReply?: boolean;
  }>();
  const navigate = useNavigate();
  const time = createTimeSource();
  const [isInTyping, setInTyping] = createSignal(false);
  const acctText = () => decodeURIComponent(params.acct);
  const session = useSessionForAcctStr(acctText);

  const pushedCount = () => {
    return location.state?.tootBottomSheetPushedCount || 0;
  };

  const [remoteToot, { mutate: setRemoteToot }] = createResource(
    () => [session().client, params.id] as const,
    async ([client, id]) => {
      return await client.v1.statuses.$select(id).fetch();
    },
  );

  const toot = () => remoteToot() ?? getCache(acctText(), params.id);

  createEffect((lastTootId?: string) => {
    const tootId = toot()?.id;
    if (!tootId || lastTootId === tootId) return tootId;
    const elementId = `toot-${tootId}`;
    document.getElementById(elementId)?.scrollIntoView({ behavior: "smooth" });
    return tootId;
  });

  createEffect(() => {
    if (location.state?.tootReply) {
      setInTyping(true);
    }
  });

  const [tootContext, { refetch: refetchContext }] = createResource(
    () => [session().client, params.id] as const,
    async ([client, id]) => {
      return await client.v1.statuses.$select(id).context.fetch();
    },
  );

  const ancestors = createTimelineControlsForArray(
    () => tootContext()?.ancestors,
  );
  const descendants = createTimelineControlsForArray(
    () => tootContext()?.descendants,
  );

  createEffect(() => {
    if (ancestors.list.length > 0) {
      document.querySelector(`#toot-${toot()!.id}`)?.scrollIntoView();
    }
  });

  useDocumentTitle(() => {
    const t = toot()?.reblog ?? toot();
    const name = t?.account.displayName ?? "Someone";
    return `${name}'s toot`;
  });

  const tootDisplayName = () => {
    const t = toot()?.reblog ?? toot();
    if (t) {
      return resolveCustomEmoji(t.account.displayName, t.account.emojis);
    }
  };

  const actSession = () => {
    const s = session();
    return s.account ? s : undefined;
  };

  const onBookmark = async () => {
    const status = remoteToot()!;
    const client = actSession()!.client;
    setRemoteToot(
      Object.assign({}, status, {
        bookmarked: !status.bookmarked,
      }),
    );
    const result = await (status.bookmarked
      ? client.v1.statuses.$select(status.id).unbookmark()
      : client.v1.statuses.$select(status.id).bookmark());
    setRemoteToot(result);
  };

  const onBoost = async () => {
    const status = remoteToot()!;
    const client = actSession()!.client;
    vibrate(50);
    setRemoteToot(
      Object.assign({}, status, {
        reblogged: !status.reblogged,
      }),
    );
    const result = await (status.reblogged
      ? client.v1.statuses.$select(status.id).unreblog()
      : client.v1.statuses.$select(status.id).reblog());
    vibrate([20, 30]);
    setRemoteToot(result.reblog!);
  };

  const onFav = async () => {
    const status = remoteToot()!;
    const client = actSession()!.client;
    setRemoteToot(
      Object.assign({}, status, {
        favourited: !status.favourited,
      }),
    );
    const result = await (status.favourited
      ? client.v1.statuses.$select(status.id).favourite()
      : client.v1.statuses.$select(status.id).unfavourite());
    setRemoteToot(result);
  };

  const switchContext = (status: mastodon.v1.Status) => {
    if (isInTyping()) {
      setInTyping(false);
      return;
    }
    setCache(params.acct, status);
    navigate(`/${params.acct}/toot/${status.id}`, {
      state: {
        tootBottomSheetPushedCount: pushedCount() + 1,
      },
    });
  };

  const defaultMentions = () => {
    const tootAcct = remoteToot()?.reblog?.account ?? remoteToot()?.account;
    if (!tootAcct) {
      return;
    }

    const others = ancestors.list.map((x) => ancestors.get(x)!.value.account);

    const values = [tootAcct, ...others].map((x) => `@${x.acct}`);
    return Array.from(new Set(values).keys());
  };

  css`
    .name :global(img) {
      max-height: 1em;
    }

    .name {
      display: grid;
      grid-template-columns: 1fr auto;
      height: calc(var(--title-size) * var(--title-line-height));

      > :first-child {
        overflow: hidden;
        white-space: nowrap;
        text-overflow: ellipsis;
      }
    }
  `;

  return (
    <Scaffold
      topbar={
        <AppBar
          sx={{
            backgroundColor: "var(--tutu-color-surface)",
            color: "var(--tutu-color-on-surface)",
          }}
          elevation={1}
          position="static"
        >
          <Toolbar
            variant="dense"
            sx={{ paddingTop: "var(--safe-area-inset-top, 0px)" }}
          >
            <IconButton color="inherit" onClick={[navigate, -1]} disableRipple>
              {pushedCount() > 0 ? <BackIcon /> : <CloseIcon />}
            </IconButton>
            <Title component="div" class="name" use:solid-styled>
              <span
                ref={(e: HTMLElement) =>
                  createRenderEffect(
                    () => (e.innerHTML = tootDisplayName() ?? "Someone"),
                  )
                }
              ></span>
              <span>'s toot</span>
            </Title>
          </Toolbar>
        </AppBar>
      }
    >
      <TimeSourceProvider value={time}>
        <TootList
          threads={ancestors.list}
          onUnknownThread={ancestors.getPath}
          onChangeToot={ancestors.set}
        />

        <article>
          <Show when={toot()}>
            <RegularToot
              id={`toot-${toot()!.id}`}
              class={cards.card}
              style={{
                "scroll-margin-top":
                  "calc(var(--scaffold-topbar-height) + 20px)",
              }}
              status={toot()!}
              actionable={!!actSession()}
              evaluated={true}
              onBookmark={onBookmark}
              onRetoot={onBoost}
              onFavourite={onFav}
            ></RegularToot>
          </Show>
        </article>

        <Show when={session()!.account}>
          <TootComposer
            isTyping={isInTyping()}
            onTypingChange={setInTyping}
            mentions={defaultMentions()}
            profile={session().account!}
            replyToDisplayName={toot()?.account?.displayName || ""}
            client={session().client}
            onSent={() => refetchContext()}
            inReplyToId={remoteToot()?.reblog?.id ?? remoteToot()?.id}
          />
        </Show>

        <Show when={tootContext.loading}>
          <div
            style={{
              display: "flex",
              "justify-content": "center",
              "margin-block": "12px",
            }}
          >
            <CircularProgress style="width: 1.5em; height: 1.5em;" />
          </div>
        </Show>

        <TootList
          threads={descendants.list}
          onUnknownThread={descendants.getPath}
          onChangeToot={descendants.set}
        />
      </TimeSourceProvider>
      <div style={{ height: "var(--safe-area-inset-bottom, 0)" }}></div>
    </Scaffold>
  );
};

export default TootBottomSheet;
