import {
  catchError,
  createRenderEffect,
  createResource,
  createSignal,
  createUniqueId,
  For,
  Switch,
  Match,
  onCleanup,
  Show,
  type Component,
  createMemo,
} from "solid-js";
import Scaffold from "../material/Scaffold";
import {
  AppBar,
  Avatar,
  Button,
  Checkbox,
  CircularProgress,
  Divider,
  IconButton,
  ListItemAvatar,
  ListItemIcon,
  ListItemSecondaryAction,
  ListItemText,
  MenuItem,
  Toolbar,
} from "@suid/material";
import {
  Close,
  Edit,
  ExpandMore,
  Group,
  MoreVert,
  OpenInBrowser,
  PersonOff,
  PlaylistAdd,
  Send,
  Share,
  SmartToySharp,
  Subject,
  Translate,
  Verified,
} from "@suid/icons-material";
import { Body2, Title } from "../material/typography";
import { useNavigate, useParams } from "@solidjs/router";
import { useSessionForAcctStr } from "../masto/clients";
import { resolveCustomEmoji } from "../masto/toot";
import { FastAverageColor } from "fast-average-color";
import { useWindowSize } from "@solid-primitives/resize-observer";
import { createTimeline, createTimelineSnapshot } from "../masto/timelines";
import TootList from "../timelines/TootList";
import { createTimeSource, TimeSourceProvider } from "../platform/timesrc";
import TootFilterButton from "./TootFilterButton";
import Menu, { createManagedMenuState } from "../material/Menu";
import { share } from "../platform/share";
import "./Profile.css";

const Profile: Component = () => {
  const navigate = useNavigate();
  const params = useParams<{ acct: string; id: string }>();
  const acctText = () => decodeURIComponent(params.acct);
  const session = useSessionForAcctStr(acctText);
  const [bannerSampledColors, setBannerSampledColors] = createSignal<{
    average: string;
    text: string;
  }>();
  const windowSize = useWindowSize();
  const time = createTimeSource();

  const menuButId = createUniqueId();
  const recentTootListId = createUniqueId();
  const optMenuId = createUniqueId();

  const [menuOpen, setMenuOpen] = createSignal(false);

  const [openSubscribeMenu, subscribeMenuState] = createManagedMenuState();

  const [scrolledPastBanner, setScrolledPastBanner] = createSignal(false);
  const obx = new IntersectionObserver(
    (entries) => {
      const ent = entries[0];
      if (ent.intersectionRatio < 0.1) {
        setScrolledPastBanner(true);
      } else {
        setScrolledPastBanner(false);
      }
    },
    {
      threshold: 0.1,
    },
  );
  onCleanup(() => obx.disconnect());

  const [profileUncaught] = createResource(
    () => [session().client, params.id] as const,
    async ([client, id]) => {
      return await client.v1.accounts.$select(id).fetch();
    },
  );

  const profile = () => {
    try {
      return profileUncaught();
    } catch (reason) {
      console.error(reason);
    }
  };

  const isCurrentSessionProfile = () => {
    return session().account?.inf?.url === profile()?.url;
  };

  const [recentTootFilter, setRecentTootFilter] = createSignal({
    pinned: true,
    boost: false,
    reply: true,
    original: true,
  });

  const [recentToots, recentTootChunk, { refetch: refetchRecentToots }] =
    createTimeline(
      () => session().client.v1.accounts.$select(params.id).statuses,
      () => {
        const { boost, reply } = recentTootFilter();
        return { limit: 20, excludeReblogs: !boost, excludeReplies: !reply };
      },
    );

  const [pinnedToots, pinnedTootChunk] = createTimelineSnapshot(
    () => session().client.v1.accounts.$select(params.id).statuses,
    () => {
      return { limit: 20, pinned: true };
    },
  );

  const [relationshipUncaught, { mutate: mutateRelationship }] = createResource(
    () => [session(), params.id] as const,
    async ([sess, id]) => {
      if (!sess.account) return; // No account, no relation
      const relations = await session().client.v1.accounts.relationships.fetch({
        id: [id],
      });
      return relations.length > 0 ? relations[0] : undefined;
    },
  );

  const relationship = () =>
    catchError(relationshipUncaught, (reason) => {
      console.error(reason);
    });

  const bannerImg = () => profile()?.header;
  const avatarImg = () => profile()?.avatar;
  const displayName = () =>
    resolveCustomEmoji(profile()?.displayName || "", profile()?.emojis ?? []);
  const fullUsername = () => (profile()?.acct ? `@${profile()!.acct!}` : ""); // TODO: full user name
  const description = () => profile()?.note;

  const isTootListLoading = () =>
    recentTootChunk.loading ||
    (recentTootFilter().pinned && pinnedTootChunk.loading);

  const sessionDisplayName = createMemo(() =>
    resolveCustomEmoji(
      session().account?.inf?.displayName || "",
      session().account?.inf?.emojis ?? [],
    ),
  );

  const useSessionDisplayName = (e: HTMLElement) => {
    createRenderEffect(() => (e.innerHTML = sessionDisplayName()));
  };

  const toggleSubscribeHome = async () => {
    const client = session().client;
    if (!session().account) return;
    const isSubscribed = relationship()?.following ?? false;
    mutateRelationship((x) => Object.assign({ following: !isSubscribed }, x));
    subscribeMenuState.onClose();

    if (isSubscribed) {
      const nrel = await client.v1.accounts.$select(params.id).unfollow();
      mutateRelationship(nrel);
    } else {
      const nrel = await client.v1.accounts.$select(params.id).follow();
      mutateRelationship(nrel);
    }
  };

  return (
    <Scaffold
      topbar={
        <AppBar
          role="navigation"
          position="static"
          color={scrolledPastBanner() ? "primary" : "transparent"}
          elevation={scrolledPastBanner() ? undefined : 0}
        >
          <Toolbar
            variant="dense"
            sx={{
              display: "flex",
              color: scrolledPastBanner()
                ? undefined
                : bannerSampledColors()?.text,
              paddingTop: "var(--safe-area-inset-top)",
            }}
          >
            <IconButton
              color="inherit"
              onClick={[navigate, -1]}
              aria-label="Close"
            >
              <Close />
            </IconButton>
            <Title
              class="Profile__page-title"
              style={{
                visibility: scrolledPastBanner() ? undefined : "hidden",
              }}
              ref={(e: HTMLElement) =>
                createRenderEffect(() => (e.innerHTML = displayName()))
              }
            ></Title>

            <IconButton
              id={menuButId}
              aria-controls={optMenuId}
              color="inherit"
              onClick={[setMenuOpen, true]}
              aria-label="Open Options for the Profile"
            >
              <MoreVert />
            </IconButton>
          </Toolbar>
        </AppBar>
      }
      class="Profile"
    >
      <Menu
        id={optMenuId}
        open={menuOpen()}
        onClose={[setMenuOpen, false]}
        anchor={() =>
          document.getElementById(menuButId)!.getBoundingClientRect()
        }
        aria-label="Options for the Profile"
      >
        <Show when={session().account}>
          <MenuItem>
            <ListItemAvatar>
              <Avatar src={session().account?.inf?.avatar} />
            </ListItemAvatar>
            <ListItemText secondary={"Default account"}>
              <span ref={useSessionDisplayName}></span>
            </ListItemText>
            {/* <ArrowRight /> // for future */}
          </MenuItem>
        </Show>
        <Show when={session().account && profile()}>
          <Show
            when={isCurrentSessionProfile()}
            fallback={
              <MenuItem
                onClick={(event) => {
                  const { left, right, top } =
                    event.currentTarget.getBoundingClientRect();
                  openSubscribeMenu({
                    left,
                    right,
                    top,
                    e: 1,
                  });
                }}
              >
                <ListItemIcon>
                  <PlaylistAdd />
                </ListItemIcon>
                <ListItemText>Subscribe...</ListItemText>
              </MenuItem>
            }
          >
            <MenuItem disabled>
              <ListItemIcon>
                <Edit />
              </ListItemIcon>
              <ListItemText>Edit...</ListItemText>
            </MenuItem>
          </Show>
          <Divider />
        </Show>
        <MenuItem disabled>
          <ListItemIcon>
            <Group />
          </ListItemIcon>
          <ListItemText>Followers</ListItemText>
          <ListItemSecondaryAction>
            <span aria-label="The number of the account follower">
              {profile()?.followersCount ?? ""}
            </span>
          </ListItemSecondaryAction>
        </MenuItem>
        <MenuItem disabled>
          <ListItemIcon>
            <Subject />
          </ListItemIcon>
          <ListItemText>Following</ListItemText>
          <ListItemSecondaryAction>
            <span aria-label="The number the account following">
              {profile()?.followingCount ?? ""}
            </span>
          </ListItemSecondaryAction>
        </MenuItem>
        <MenuItem disabled>
          <ListItemIcon>
            <PersonOff />
          </ListItemIcon>
          <ListItemText>Blocklist</ListItemText>
        </MenuItem>
        <MenuItem disabled>
          <ListItemIcon>
            <Send />
          </ListItemIcon>
          <ListItemText>Mention in...</ListItemText>
        </MenuItem>
        <Divider />
        <MenuItem
          component={"a"}
          href={profile()?.url}
          target="_blank"
          rel="noopener noreferrer"
        >
          <ListItemIcon>
            <OpenInBrowser />
          </ListItemIcon>
          <ListItemText>Open in browser...</ListItemText>
        </MenuItem>
        <MenuItem onClick={() => share({ url: profile()?.url })}>
          <ListItemIcon>
            <Share />
          </ListItemIcon>
          <ListItemText>Share...</ListItemText>
        </MenuItem>
      </Menu>
      <div
        style={{
          height: `${268 * (Math.min(560, windowSize.width) / 560)}px`,
        }}
        class="banner"
        role="presentation"
      >
        <img
          ref={(e) => obx.observe(e)}
          src={bannerImg()}
          crossOrigin="anonymous"
          alt={`Banner image for ${profile()?.displayName || "the user"}`}
          onLoad={(event) => {
            const ins = new FastAverageColor();
            const colors = ins.getColor(event.currentTarget);
            setBannerSampledColors({
              average: colors.hex,
              text: colors.isDark ? "white" : "black",
            });
            ins.destroy();
          }}
        ></img>
      </div>

      <Menu {...subscribeMenuState}>
        <MenuItem
          onClick={toggleSubscribeHome}
          aria-details="Subscribe or Unsubscribe this account on your home timeline"
        >
          <ListItemAvatar>
            <Avatar src={session().account?.inf?.avatar}></Avatar>
          </ListItemAvatar>
          <ListItemText
            secondary={relationship()?.following ? "Subscribed" : undefined}
          >
            <span ref={useSessionDisplayName}></span>
            <span>'s Home</span>
          </ListItemText>

          <Checkbox checked={relationship()?.following ?? false} />
        </MenuItem>
      </Menu>

      <div
        class="intro"
        style={{
          "background-color": bannerSampledColors()?.average,
          color: bannerSampledColors()?.text,
        }}
      >
        <section class="acct-grp">
          <Avatar
            src={avatarImg()}
            alt={`${profile()?.displayName || "the user"}'s avatar`}
            sx={{
              marginTop: "calc(-16px - 72px / 2)",
              width: "72px",
              height: "72px",
            }}
          ></Avatar>
          <div class="name-grp">
            <div class="display-name">
              <Show when={profile()?.bot}>
                <SmartToySharp class="bot-mark" aria-label="Bot" />
              </Show>
              <Body2
                component="span"
                ref={(e: HTMLElement) =>
                  createRenderEffect(() => (e.innerHTML = displayName()))
                }
                aria-label="Display name"
              ></Body2>
            </div>
            <span aria-label="Complete username">{fullUsername()}</span>
          </div>
          <div role="presentation">
            <Switch>
              <Match
                when={
                  !session().account ||
                  profileUncaught.loading ||
                  profileUncaught.error
                }
              >
                {<></>}
              </Match>
              <Match when={isCurrentSessionProfile()}>
                <IconButton color="inherit">
                  <Edit />
                </IconButton>
              </Match>
              <Match when={true}>
                <Button
                  variant="contained"
                  color="secondary"
                  onClick={(event) => {
                    openSubscribeMenu(
                      event.currentTarget.getBoundingClientRect(),
                    );
                  }}
                >
                  {relationship()?.following ? "Subscribed" : "Subscribe"}
                </Button>
              </Match>
            </Switch>
          </div>
        </section>
        <section
          class="description"
          aria-label={`${profile()?.displayName || "the user"}'s description`}
          ref={(e) =>
            createRenderEffect(() => (e.innerHTML = description() || ""))
          }
        ></section>

        <table
          class="acct-fields"
          aria-label={`${profile()?.displayName || "the user"}'s fields`}
        >
          <tbody>
            <For each={profile()?.fields ?? []}>
              {(item, index) => {
                return (
                  <tr data-field-index={index()}>
                    <td>{item.name}</td>
                    <td>
                      <Show when={item.verifiedAt}>
                        <Verified />
                      </Show>
                    </td>
                    <td
                      ref={(e) => {
                        createRenderEffect(() => (e.innerHTML = item.value));
                      }}
                    ></td>
                  </tr>
                );
              }}
            </For>
          </tbody>
        </table>
      </div>

      <div class="toot-list-toolbar">
        <TootFilterButton
          options={{
            pinned: "Pinneds",
            boost: "Boosts",
            reply: "Replies",
            original: "Originals",
          }}
          applied={recentTootFilter()}
          onApply={setRecentTootFilter}
          disabledKeys={["original"]}
        ></TootFilterButton>
      </div>

      <TimeSourceProvider value={time}>
        <Show when={recentTootFilter().pinned && pinnedToots.list.length > 0}>
          <TootList
            threads={pinnedToots.list}
            onUnknownThread={pinnedToots.getPath}
            onChangeToot={pinnedToots.set}
          />
          <Divider />
        </Show>
        <TootList
          id={recentTootListId}
          threads={recentToots.list}
          onUnknownThread={recentToots.getPath}
          onChangeToot={recentToots.set}
        />
      </TimeSourceProvider>

      <Show when={!recentTootChunk()?.done}>
        <div
          style={{
            "text-align": "center",
            "padding-bottom": "var(--safe-area-inset-bottom)",
          }}
        >
          <IconButton
            aria-label="Load More"
            aria-controls={recentTootListId}
            size="large"
            color="primary"
            onClick={[refetchRecentToots, "prev"]}
            disabled={isTootListLoading()}
          >
            <Show when={isTootListLoading()} fallback={<ExpandMore />}>
              <CircularProgress sx={{ width: "24px", height: "24px" }} />
            </Show>
          </IconButton>
        </div>
      </Show>
    </Scaffold>
  );
};

export default Profile;
