import {
  catchError,
  createResource,
  createSignal,
  createUniqueId,
  For,
  Switch,
  Match,
  onCleanup,
  Show,
  type Component,
  createMemo,
} from "solid-js";
import Scaffold from "~material/Scaffold";
import {
  Avatar,
  Button,
  Checkbox,
  CircularProgress,
  Divider,
  IconButton,
  ListItemAvatar,
  ListItemIcon,
  ListItemSecondaryAction,
  ListItemText,
  MenuItem,
} from "@suid/material";
import {
  Close,
  Edit,
  ExpandMore,
  Group,
  Lock,
  MoreVert,
  OpenInBrowser,
  PersonOff,
  PlaylistAdd,
  Send,
  Share,
  SmartToySharp,
  Subject,
  Verified,
} from "@suid/icons-material";
import { Body2, Title } from "~material/typography";
import { 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,
  emptyTimeline,
} 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";
import { useNavigator } from "~platform/StackedRouter";
import {
  createSingluarItemSelection,
  default as ItemSelectionProvider,
} from "../timelines/toots/ItemSelectionProvider";
import AppTopBar from "~material/AppTopBar";
import type { Account } from "../accounts/stores";

const Profile: Component = () => {
  const { pop } = useNavigator();
  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 [, selectionState] = createSingluarItemSelection();

  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]) => {
      if (id.startsWith("@")) {
        return await client.v1.accounts.lookup({ acct: id.slice(1) });
      }
      return await client.v1.accounts.$select(id).fetch();
    },
  );

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

  const profileAcctId = () => {
    if (params.id.startsWith("@")) {
      // Webfinger
      return profile()?.id;
    } else {
      return params.id;
    }
  };

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

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

  const recentTimeline = () => {
    const id = profileAcctId();

    if (id) {
      return session().client.v1.accounts.$select(id).statuses;
    } else {
      return emptyTimeline;
    }
  };

  const [recentToots, recentTootChunk, { refetch: refetchRecentToots }] =
    createTimeline(recentTimeline, () => {
      const { boost, reply } = recentTootFilter();
      return { limit: 20, excludeReblogs: !boost, excludeReplies: !reply };
    });

  const [pinnedToots, pinnedTootChunk] = createTimelineSnapshot(
    recentTimeline,
    () => {
      return { limit: 20, pinned: true };
    },
  );

  const [relationshipUncaught, { mutate: mutateRelationship }] = createResource(
    () => [session(), profileAcctId()] as const,
    async ([sess, id]) => {
      if (!sess.account || !id) 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 as Account).inf?.displayName || "",
      (session().account as Account).inf?.emojis ?? [],
    ),
  );

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

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

  return (
    <Scaffold
      topbar={
        <AppTopBar
          role="navigation"
          position="static"
          color={scrolledPastBanner() ? "primary" : "transparent"}
          elevation={scrolledPastBanner() ? undefined : 0}
          style={{
            color: scrolledPastBanner()
              ? undefined
              : bannerSampledColors()?.text,
          }}
        >
          <IconButton color="inherit" onClick={[pop, 1]} aria-label="Close">
            <Close />
          </IconButton>
          <Title
            class="Profile__page-title"
            style={{
              visibility: scrolledPastBanner() ? undefined : "hidden",
            }}
            innerHTML={displayName()}
          ></Title>

          <IconButton
            id={menuButId}
            aria-controls={optMenuId}
            color="inherit"
            onClick={[setMenuOpen, true]}
            aria-label="Open Options for the Profile"
          >
            <MoreVert />
          </IconButton>
        </AppTopBar>
      }
      class="Profile"
    >
      <div class="details" role="presentation">
        <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 as Account).inf?.avatar} />
              </ListItemAvatar>
              <ListItemText secondary={"Default account"}>
                <span innerHTML={sessionDisplayName()}></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-label={`${relationship()?.following ? "Unfollow" : "Follow"} on your home timeline`}
          >
            <ListItemAvatar>
              <Avatar src={(session().account as Account).inf?.avatar}></Avatar>
            </ListItemAvatar>
            <ListItemText
              secondary={
                relationship()?.following
                  ? undefined
                  : profile()?.locked
                    ? "A request will be sent"
                    : undefined
              }
            >
              <span innerHTML={sessionDisplayName()}></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="acct-mark" aria-label="Bot" />
                </Show>
                <Show when={profile()?.locked}>
                  <Lock class="acct-mark" aria-label="Locked" />
                </Show>
                <Body2
                  component="span"
                  innerHTML={displayName()}
                  aria-label="Display name"
                ></Body2>
              </div>
              <span aria-label="Complete username" class="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`}
            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 innerHTML={item.value}></td>
                    </tr>
                  );
                }}
              </For>
            </tbody>
          </table>
        </div>
      </div>

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

        <ItemSelectionProvider value={selectionState}>
          <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>
        </ItemSelectionProvider>

        <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>
      </div>
    </Scaffold>
  );
};

export default Profile;
