import './account-info.css';

import { msg, plural, t, Trans } from '@lingui/macro';
import { useLingui } from '@lingui/react';
import { MenuDivider, MenuItem } from '@szhsin/react-menu';
import {
  useCallback,
  useEffect,
  useMemo,
  useReducer,
  useRef,
  useState,
} from 'preact/hooks';
import punycode from 'punycode/';

import { api } from '../utils/api';
import enhanceContent from '../utils/enhance-content';
import getHTMLText from '../utils/getHTMLText';
import handleContentLinks from '../utils/handle-content-links';
import i18nDuration from '../utils/i18n-duration';
import { getLists } from '../utils/lists';
import niceDateTime from '../utils/nice-date-time';
import pmem from '../utils/pmem';
import shortenNumber from '../utils/shorten-number';
import showCompose from '../utils/show-compose';
import showToast from '../utils/show-toast';
import states, { hideAllModals } from '../utils/states';
import store from '../utils/store';
import { getCurrentAccountID, updateAccount } from '../utils/store-utils';
import supports from '../utils/supports';

import AccountBlock from './account-block';
import Avatar from './avatar';
import EmojiText from './emoji-text';
import Icon from './icon';
import Link from './link';
import ListAddEdit from './list-add-edit';
import Loader from './loader';
import MenuConfirm from './menu-confirm';
import MenuLink from './menu-link';
import Menu2 from './menu2';
import Modal from './modal';
import SubMenu2 from './submenu2';
import TranslationBlock from './translation-block';

const MUTE_DURATIONS = [
  60 * 5, // 5 minutes
  60 * 30, // 30 minutes
  60 * 60, // 1 hour
  60 * 60 * 6, // 6 hours
  60 * 60 * 24, // 1 day
  60 * 60 * 24 * 3, // 3 days
  60 * 60 * 24 * 7, // 1 week
  0, // forever
];
const MUTE_DURATIONS_LABELS = {
  0: msg`Forever`,
  300: i18nDuration(5, 'minute'),
  1_800: i18nDuration(30, 'minute'),
  3_600: i18nDuration(1, 'hour'),
  21_600: i18nDuration(6, 'hour'),
  86_400: i18nDuration(1, 'day'),
  259_200: i18nDuration(3, 'day'),
  604_800: i18nDuration(1, 'week'),
};

const LIMIT = 80;

const ACCOUNT_INFO_MAX_AGE = 1000 * 60 * 10; // 10 mins

function fetchFamiliarFollowers(currentID, masto) {
  return masto.v1.accounts.familiarFollowers.fetch({
    id: [currentID],
  });
}
const memFetchFamiliarFollowers = pmem(fetchFamiliarFollowers, {
  maxAge: ACCOUNT_INFO_MAX_AGE,
});

async function fetchPostingStats(accountID, masto) {
  const fetchStatuses = masto.v1.accounts
    .$select(accountID)
    .statuses.list({
      limit: 20,
    })
    .next();

  const { value: statuses } = await fetchStatuses;
  console.log('fetched statuses', statuses);
  const stats = {
    total: statuses.length,
    originals: 0,
    replies: 0,
    boosts: 0,
  };
  // Categories statuses by type
  // - Original posts (not replies to others)
  // - Threads (self-replies + 1st original post)
  // - Boosts (reblogs)
  // - Replies (not-self replies)
  statuses.forEach((status) => {
    if (status.reblog) {
      stats.boosts++;
    } else if (
      !!status.inReplyToId &&
      status.inReplyToAccountId !== status.account.id // Not self-reply
    ) {
      stats.replies++;
    } else {
      stats.originals++;
    }
  });

  // Count days since last post
  if (statuses.length) {
    stats.daysSinceLastPost = Math.ceil(
      (Date.now() - new Date(statuses[statuses.length - 1].createdAt)) /
        86400000,
    );
  }

  console.log('posting stats', stats);
  return stats;
}
const memFetchPostingStats = pmem(fetchPostingStats, {
  maxAge: ACCOUNT_INFO_MAX_AGE,
});

function AccountInfo({
  account,
  fetchAccount = () => {},
  standalone,
  instance,
  authenticated,
}) {
  const { i18n } = useLingui();
  const { masto } = api({
    instance,
  });
  const { masto: currentMasto, instance: currentInstance } = api();
  const [uiState, setUIState] = useState('default');
  const isString = typeof account === 'string';
  const [info, setInfo] = useState(isString ? null : account);

  const sameCurrentInstance = useMemo(
    () => instance === currentInstance,
    [instance, currentInstance],
  );

  useEffect(() => {
    if (!isString) {
      setInfo(account);
      return;
    }
    setUIState('loading');
    (async () => {
      try {
        const info = await fetchAccount();
        states.accounts[`${info.id}@${instance}`] = info;
        setInfo(info);
        setUIState('default');
      } catch (e) {
        console.error(e);
        setInfo(null);
        setUIState('error');
      }
    })();
  }, [isString, account, fetchAccount]);

  const {
    acct,
    avatar,
    avatarStatic,
    bot,
    createdAt,
    displayName,
    emojis,
    fields,
    followersCount,
    followingCount,
    group,
    // header,
    // headerStatic,
    id,
    lastStatusAt,
    locked,
    note,
    statusesCount,
    url,
    username,
    memorial,
    moved,
    roles,
    hideCollections,
  } = info || {};
  let headerIsAvatar = false;
  let { header, headerStatic } = info || {};
  if (!header || /missing\.png$/.test(header)) {
    if (avatar && !/missing\.png$/.test(avatar)) {
      header = avatar;
      headerIsAvatar = true;
      if (avatarStatic && !/missing\.png$/.test(avatarStatic)) {
        headerStatic = avatarStatic;
      }
    }
  }

  const isSelf = useMemo(() => id === getCurrentAccountID(), [id]);

  useEffect(() => {
    const infoHasEssentials = !!(
      info?.id &&
      info?.username &&
      info?.acct &&
      info?.avatar &&
      info?.avatarStatic &&
      info?.displayName &&
      info?.url
    );
    if (isSelf && instance && infoHasEssentials) {
      const accounts = store.local.getJSON('accounts');
      let updated = false;
      accounts.forEach((account) => {
        if (account.info.id === info.id && account.instanceURL === instance) {
          account.info = info;
          updated = true;
        }
      });
      if (updated) {
        console.log('Updated account info', info);
        store.local.setJSON('accounts', accounts);
      }
    }
  }, [isSelf, info, instance]);

  const accountInstance = useMemo(() => {
    if (!url) return null;
    const domain = punycode.toUnicode(URL.parse(url).hostname);
    return domain;
  }, [url]);

  const [headerCornerColors, setHeaderCornerColors] = useState([]);

  const followersIterator = useRef();
  const familiarFollowersCache = useRef([]);
  async function fetchFollowers(firstLoad) {
    if (firstLoad || !followersIterator.current) {
      followersIterator.current = masto.v1.accounts.$select(id).followers.list({
        limit: LIMIT,
      });
    }
    const results = await followersIterator.current.next();
    if (isSelf) return results;
    if (!sameCurrentInstance) return results;

    const { value } = results;
    let newValue = [];
    // On first load, fetch familiar followers, merge to top of results' `value`
    // Remove dups on every fetch
    if (firstLoad) {
      let familiarFollowers = [];
      try {
        familiarFollowers = await masto.v1.accounts.familiarFollowers.fetch({
          id: [id],
        });
      } catch (e) {}
      familiarFollowersCache.current = familiarFollowers?.[0]?.accounts || [];
      newValue = [
        ...familiarFollowersCache.current,
        ...value.filter(
          (account) =>
            !familiarFollowersCache.current.some(
              (familiar) => familiar.id === account.id,
            ),
        ),
      ];
    } else if (value?.length) {
      newValue = value.filter(
        (account) =>
          !familiarFollowersCache.current.some(
            (familiar) => familiar.id === account.id,
          ),
      );
    }

    return {
      ...results,
      value: newValue,
    };
  }

  const followingIterator = useRef();
  async function fetchFollowing(firstLoad) {
    if (firstLoad || !followingIterator.current) {
      followingIterator.current = masto.v1.accounts.$select(id).following.list({
        limit: LIMIT,
      });
    }
    const results = await followingIterator.current.next();
    return results;
  }

  const LinkOrDiv = standalone ? 'div' : Link;
  const accountLink = instance ? `/${instance}/a/${id}` : `/a/${id}`;

  const [familiarFollowers, setFamiliarFollowers] = useState([]);
  const [postingStats, setPostingStats] = useState();
  const [postingStatsUIState, setPostingStatsUIState] = useState('default');
  const hasPostingStats = !!postingStats?.total;

  const renderFamiliarFollowers = async (currentID) => {
    try {
      const followers = await memFetchFamiliarFollowers(
        currentID,
        currentMasto,
      );
      console.log('fetched familiar followers', followers);
      setFamiliarFollowers(
        followers[0].accounts.slice(0, FAMILIAR_FOLLOWERS_LIMIT),
      );
    } catch (e) {
      console.error(e);
    }
  };

  const renderPostingStats = async () => {
    if (!id) return;
    setPostingStatsUIState('loading');
    try {
      const stats = await memFetchPostingStats(id, masto);
      setPostingStats(stats);
      setPostingStatsUIState('default');
    } catch (e) {
      console.error(e);
      setPostingStatsUIState('error');
    }
  };

  const onRelationshipChange = useCallback(
    ({ relationship, currentID }) => {
      if (!relationship.following) {
        renderFamiliarFollowers(currentID);
        if (!standalone && statusesCount > 0) {
          // Only render posting stats if not standalone and has posts
          renderPostingStats();
        }
      }
    },
    [standalone, id, statusesCount],
  );

  const onProfileUpdate = useCallback(
    (newAccount) => {
      if (newAccount.id === id) {
        console.log('Updated account info', newAccount);
        setInfo(newAccount);
        states.accounts[`${newAccount.id}@${instance}`] = newAccount;
      }
    },
    [id, instance],
  );

  return (
    <div
      tabIndex="-1"
      class={`account-container ${uiState === 'loading' ? 'skeleton' : ''}`}
      style={{
        '--header-color-1': headerCornerColors[0],
        '--header-color-2': headerCornerColors[1],
        '--header-color-3': headerCornerColors[2],
        '--header-color-4': headerCornerColors[3],
      }}
    >
      {uiState === 'error' && (
        <div class="ui-state">
          <p>
            <Trans>Unable to load account.</Trans>
          </p>
          <p>
            <a
              href={isString ? account : url}
              target="_blank"
              rel="noopener noreferrer"
            >
              <Trans>Go to account page</Trans> <Icon icon="external" />
            </a>
          </p>
        </div>
      )}
      {uiState === 'loading' ? (
        <>
          <header>
            <AccountBlock avatarSize="xxxl" skeleton />
          </header>
          <main>
            <div class="note">
              <p>███████ ████ ████</p>
              <p>████ ████████ ██████ █████████ ████ ██</p>
            </div>
            <div class="account-metadata-box">
              <div class="profile-metadata">
                <div class="profile-field">
                  <b class="more-insignificant">███</b>
                  <p>██████</p>
                </div>
                <div class="profile-field">
                  <b class="more-insignificant">████</b>
                  <p>███████████</p>
                </div>
              </div>
              <div class="stats">
                <div>
                  <span>██</span> <Trans>Followers</Trans>
                </div>
                <div>
                  <span>██</span> <Trans>Following</Trans>
                </div>
                <div>
                  <span>██</span> <Trans>Posts</Trans>
                </div>
              </div>
            </div>
            <div class="actions">
              <span />
              <span class="buttons">
                <button type="button" class="plain" disabled>
                  <Icon icon="more" size="l" alt={t`More`} />
                </button>
              </span>
            </div>
          </main>
        </>
      ) : (
        info && (
          <>
            {!!moved && (
              <div class="account-moved">
                <p>
                  <Trans>
                    <b>{displayName}</b> has indicated that their new account is
                    now:
                  </Trans>
                </p>
                <AccountBlock
                  account={moved}
                  instance={instance}
                  onClick={(e) => {
                    e.stopPropagation();
                    states.showAccount = moved;
                  }}
                />
              </div>
            )}
            {!!header && !/missing\.png$/.test(header) && (
              <img
                src={header}
                alt=""
                class={`header-banner ${
                  headerIsAvatar ? 'header-is-avatar' : ''
                }`}
                onError={(e) => {
                  if (e.target.crossOrigin) {
                    if (e.target.src !== headerStatic) {
                      e.target.src = headerStatic;
                    } else {
                      e.target.removeAttribute('crossorigin');
                      e.target.src = header;
                    }
                  } else if (e.target.src !== headerStatic) {
                    e.target.src = headerStatic;
                  } else {
                    e.target.remove();
                  }
                }}
                crossOrigin="anonymous"
                onLoad={(e) => {
                  e.target.classList.add('loaded');
                  try {
                    // Get color from four corners of image
                    const canvas = window.OffscreenCanvas
                      ? new OffscreenCanvas(1, 1)
                      : document.createElement('canvas');
                    const ctx = canvas.getContext('2d', {
                      willReadFrequently: true,
                    });
                    canvas.width = e.target.width;
                    canvas.height = e.target.height;
                    ctx.imageSmoothingEnabled = false;
                    ctx.drawImage(e.target, 0, 0);
                    // const colors = [
                    //   ctx.getImageData(0, 0, 1, 1).data,
                    //   ctx.getImageData(e.target.width - 1, 0, 1, 1).data,
                    //   ctx.getImageData(0, e.target.height - 1, 1, 1).data,
                    //   ctx.getImageData(
                    //     e.target.width - 1,
                    //     e.target.height - 1,
                    //     1,
                    //     1,
                    //   ).data,
                    // ];
                    // Get 10x10 pixels from corners, get average color from each
                    const pixelDimension = 10;
                    const colors = [
                      ctx.getImageData(0, 0, pixelDimension, pixelDimension)
                        .data,
                      ctx.getImageData(
                        e.target.width - pixelDimension,
                        0,
                        pixelDimension,
                        pixelDimension,
                      ).data,
                      ctx.getImageData(
                        0,
                        e.target.height - pixelDimension,
                        pixelDimension,
                        pixelDimension,
                      ).data,
                      ctx.getImageData(
                        e.target.width - pixelDimension,
                        e.target.height - pixelDimension,
                        pixelDimension,
                        pixelDimension,
                      ).data,
                    ].map((data) => {
                      let r = 0;
                      let g = 0;
                      let b = 0;
                      let a = 0;
                      for (let i = 0; i < data.length; i += 4) {
                        r += data[i];
                        g += data[i + 1];
                        b += data[i + 2];
                        a += data[i + 3];
                      }
                      const dataLength = data.length / 4;
                      return [
                        r / dataLength,
                        g / dataLength,
                        b / dataLength,
                        a / dataLength,
                      ];
                    });
                    const rgbColors = colors.map((color) => {
                      const [r, g, b, a] = lightenRGB(color);
                      return `rgba(${r}, ${g}, ${b}, ${a})`;
                    });
                    setHeaderCornerColors(rgbColors);
                    console.log({ colors, rgbColors });
                  } catch (e) {
                    // Silently fail
                  }
                }}
              />
            )}
            <header>
              {standalone ? (
                <Menu2
                  shift={
                    window.matchMedia('(min-width: calc(40em))').matches
                      ? 114
                      : 64
                  }
                  menuButton={
                    <div>
                      <AccountBlock
                        account={info}
                        instance={instance}
                        avatarSize="xxxl"
                        onClick={() => {}}
                      />
                    </div>
                  }
                >
                  <div class="szh-menu__header">
                    <AccountHandleInfo acct={acct} instance={instance} />
                  </div>
                  <MenuItem
                    onClick={() => {
                      const handleWithInstance = acct.includes('@')
                        ? `@${acct}`
                        : `@${acct}@${instance}`;
                      try {
                        navigator.clipboard.writeText(handleWithInstance);
                        showToast(t`Handle copied`);
                      } catch (e) {
                        console.error(e);
                        showToast(t`Unable to copy handle`);
                      }
                    }}
                  >
                    <Icon icon="link" />
                    <span>
                      <Trans>Copy handle</Trans>
                    </span>
                  </MenuItem>
                  <MenuItem href={url} target="_blank">
                    <Icon icon="external" />
                    <span>
                      <Trans>Go to original profile page</Trans>
                    </span>
                  </MenuItem>
                  <MenuDivider />
                  <MenuLink href={info.avatar} target="_blank">
                    <Icon icon="user" />
                    <span>
                      <Trans>View profile image</Trans>
                    </span>
                  </MenuLink>
                  <MenuLink href={info.header} target="_blank">
                    <Icon icon="media" />
                    <span>
                      <Trans>View profile header</Trans>
                    </span>
                  </MenuLink>
                </Menu2>
              ) : (
                <AccountBlock
                  account={info}
                  instance={instance}
                  avatarSize="xxxl"
                  internal
                />
              )}
            </header>
            <div class="faux-header-bg" aria-hidden="true" />
            <main>
              {!!memorial && (
                <span class="tag">
                  <Trans>In Memoriam</Trans>
                </span>
              )}
              {!!bot && (
                <span class="tag">
                  <Icon icon="bot" /> <Trans>Automated</Trans>
                </span>
              )}
              {!!group && (
                <span class="tag">
                  <Icon icon="group" /> <Trans>Group</Trans>
                </span>
              )}
              {roles?.map((role) => (
                <span class="tag">
                  {role.name}
                  {!!accountInstance && (
                    <>
                      {' '}
                      <span class="more-insignificant">{accountInstance}</span>
                    </>
                  )}
                </span>
              ))}
              <div
                class="note"
                dir="auto"
                onClick={handleContentLinks({
                  instance: currentInstance,
                })}
                dangerouslySetInnerHTML={{
                  __html: enhanceContent(note, { emojis }),
                }}
              />
              <div class="account-metadata-box">
                {fields?.length > 0 && (
                  <div class="profile-metadata">
                    {fields.map(({ name, value, verifiedAt }, i) => (
                      <div
                        class={`profile-field ${
                          verifiedAt ? 'profile-verified' : ''
                        }`}
                        key={name + i}
                        dir="auto"
                      >
                        <b>
                          <EmojiText text={name} emojis={emojis} />{' '}
                          {!!verifiedAt && (
                            <Icon
                              icon="check-circle"
                              size="s"
                              alt={t`Verified`}
                            />
                          )}
                        </b>
                        <p
                          dangerouslySetInnerHTML={{
                            __html: enhanceContent(value, { emojis }),
                          }}
                        />
                      </div>
                    ))}
                  </div>
                )}
                <div class="stats">
                  <LinkOrDiv
                    tabIndex={0}
                    to={accountLink}
                    onClick={() => {
                      // states.showAccount = false;
                      setTimeout(() => {
                        states.showGenericAccounts = {
                          id: 'followers',
                          heading: t`Followers`,
                          fetchAccounts: fetchFollowers,
                          instance,
                          excludeRelationshipAttrs: isSelf
                            ? ['followedBy']
                            : [],
                          blankCopy: hideCollections
                            ? t`This user has chosen to not make this information available.`
                            : undefined,
                        };
                      }, 0);
                    }}
                  >
                    {!!familiarFollowers.length && (
                      <span class="shazam-container-horizontal">
                        <span class="shazam-container-inner stats-avatars-bunch">
                          {familiarFollowers.map((follower) => (
                            <Avatar
                              url={follower.avatarStatic}
                              size="s"
                              alt={`${follower.displayName} @${follower.acct}`}
                              squircle={follower?.bot}
                            />
                          ))}
                        </span>
                      </span>
                    )}
                    <span title={followersCount}>
                      {shortenNumber(followersCount)}
                    </span>{' '}
                    <Trans>Followers</Trans>
                  </LinkOrDiv>
                  <LinkOrDiv
                    class="insignificant"
                    tabIndex={0}
                    to={accountLink}
                    onClick={() => {
                      // states.showAccount = false;
                      setTimeout(() => {
                        states.showGenericAccounts = {
                          heading: t`Following`,
                          fetchAccounts: fetchFollowing,
                          instance,
                          excludeRelationshipAttrs: isSelf ? ['following'] : [],
                          blankCopy: hideCollections
                            ? t`This user has chosen to not make this information available.`
                            : undefined,
                        };
                      }, 0);
                    }}
                  >
                    <span title={followingCount}>
                      {shortenNumber(followingCount)}
                    </span>{' '}
                    <Trans>Following</Trans>
                    <br />
                  </LinkOrDiv>
                  <LinkOrDiv
                    class="insignificant"
                    to={accountLink}
                    // onClick={
                    //   standalone
                    //     ? undefined
                    //     : () => {
                    //         hideAllModals();
                    //       }
                    // }
                  >
                    <span title={statusesCount}>
                      {shortenNumber(statusesCount)}
                    </span>{' '}
                    <Trans>Posts</Trans>
                  </LinkOrDiv>
                  {!!createdAt && (
                    <div class="insignificant">
                      <Trans>
                        Joined{' '}
                        <time datetime={createdAt}>
                          {niceDateTime(createdAt, {
                            hideTime: true,
                          })}
                        </time>
                      </Trans>
                    </div>
                  )}
                </div>
              </div>
              {!!postingStats && (
                <LinkOrDiv
                  to={accountLink}
                  class="account-metadata-box"
                  // onClick={() => {
                  //   states.showAccount = false;
                  // }}
                >
                  <div class="shazam-container">
                    <div class="shazam-container-inner">
                      {hasPostingStats ? (
                        <div
                          class="posting-stats"
                          title={t`${(
                            postingStats.originals / postingStats.total
                          ).toLocaleString(i18n.locale || undefined, {
                            style: 'percent',
                          })} original posts, ${(
                            postingStats.replies / postingStats.total
                          ).toLocaleString(i18n.locale || undefined, {
                            style: 'percent',
                          })} replies, ${(
                            postingStats.boosts / postingStats.total
                          ).toLocaleString(i18n.locale || undefined, {
                            style: 'percent',
                          })} boosts`}
                        >
                          <div>
                            {postingStats.daysSinceLastPost < 365
                              ? plural(postingStats.total, {
                                  one: plural(postingStats.daysSinceLastPost, {
                                    one: `Last 1 post in the past 1 day`,
                                    other: `Last 1 post in the past ${postingStats.daysSinceLastPost} days`,
                                  }),
                                  other: plural(
                                    postingStats.daysSinceLastPost,
                                    {
                                      one: `Last ${postingStats.total} posts in the past 1 day`,
                                      other: `Last ${postingStats.total} posts in the past ${postingStats.daysSinceLastPost} days`,
                                    },
                                  ),
                                })
                              : plural(postingStats.total, {
                                  one: 'Last 1 post in the past year(s)',
                                  other: `Last ${postingStats.total} posts in the past year(s)`,
                                })}
                          </div>
                          <div
                            class="posting-stats-bar"
                            style={{
                              // [originals | replies | boosts]
                              '--originals-percentage': `${
                                (postingStats.originals / postingStats.total) *
                                100
                              }%`,
                              '--replies-percentage': `${
                                ((postingStats.originals +
                                  postingStats.replies) /
                                  postingStats.total) *
                                100
                              }%`,
                            }}
                          />
                          <div class="posting-stats-legends">
                            <span class="ib">
                              <span class="posting-stats-legend-item posting-stats-legend-item-originals" />{' '}
                              <Trans>Original</Trans>
                            </span>{' '}
                            <span class="ib">
                              <span class="posting-stats-legend-item posting-stats-legend-item-replies" />{' '}
                              <Trans>Replies</Trans>
                            </span>{' '}
                            <span class="ib">
                              <span class="posting-stats-legend-item posting-stats-legend-item-boosts" />{' '}
                              <Trans>Boosts</Trans>
                            </span>
                          </div>
                        </div>
                      ) : (
                        <div class="posting-stats">
                          <Trans>Post stats unavailable.</Trans>
                        </div>
                      )}
                    </div>
                  </div>
                </LinkOrDiv>
              )}
              {!moved && (
                <div class="account-metadata-box">
                  <div
                    class="shazam-container no-animation"
                    hidden={!!postingStats}
                  >
                    <div class="shazam-container-inner">
                      <button
                        type="button"
                        class="posting-stats-button"
                        disabled={postingStatsUIState === 'loading'}
                        onClick={() => {
                          renderPostingStats();
                        }}
                      >
                        <div
                          class={`posting-stats-bar posting-stats-icon ${
                            postingStatsUIState === 'loading' ? 'loading' : ''
                          }`}
                          style={{
                            '--originals-percentage': '33%',
                            '--replies-percentage': '66%',
                          }}
                        />
                        <Trans>View post stats</Trans>{' '}
                        {/* <Loader
                        abrupt
                        hidden={postingStatsUIState !== 'loading'}
                      /> */}
                      </button>
                    </div>
                  </div>
                </div>
              )}
            </main>
            <footer>
              <RelatedActions
                info={info}
                instance={instance}
                standalone={standalone}
                authenticated={authenticated}
                onRelationshipChange={onRelationshipChange}
                onProfileUpdate={onProfileUpdate}
              />
            </footer>
          </>
        )
      )}
    </div>
  );
}

const FAMILIAR_FOLLOWERS_LIMIT = 3;

function RelatedActions({
  info,
  instance,
  standalone,
  authenticated,
  onRelationshipChange = () => {},
  onProfileUpdate = () => {},
}) {
  if (!info) return null;
  const { _ } = useLingui();
  const {
    masto: currentMasto,
    instance: currentInstance,
    authenticated: currentAuthenticated,
  } = api();
  const sameInstance = instance === currentInstance;

  const [relationshipUIState, setRelationshipUIState] = useState('default');
  const [relationship, setRelationship] = useState(null);

  const { id, acct, url, username, locked, lastStatusAt, note, fields, moved } =
    info;
  const accountID = useRef(id);

  const {
    following,
    showingReblogs,
    notifying,
    followedBy,
    blocking,
    blockedBy,
    muting,
    mutingNotifications,
    requested,
    domainBlocking,
    endorsed,
    note: privateNote,
  } = relationship || {};

  const [currentInfo, setCurrentInfo] = useState(null);
  const [isSelf, setIsSelf] = useState(false);

  const acctWithInstance = acct.includes('@') ? acct : `${acct}@${instance}`;

  useEffect(() => {
    if (info) {
      const currentAccount = getCurrentAccountID();
      let currentID;
      (async () => {
        if (sameInstance && authenticated) {
          currentID = id;
        } else if (!sameInstance && currentAuthenticated) {
          // Grab this account from my logged-in instance
          const acctHasInstance = info.acct.includes('@');
          try {
            const results = await currentMasto.v2.search.fetch({
              q: acctHasInstance ? info.acct : `${info.username}@${instance}`,
              type: 'accounts',
              limit: 1,
              resolve: true,
            });
            console.log('🥏 Fetched account from logged-in instance', results);
            if (results.accounts.length) {
              currentID = results.accounts[0].id;
              setCurrentInfo(results.accounts[0]);
            }
          } catch (e) {
            console.error(e);
          }
        }

        if (!currentID) return;

        if (currentAccount === currentID) {
          // It's myself!
          setIsSelf(true);
          return;
        }

        accountID.current = currentID;

        // if (moved) return;

        setRelationshipUIState('loading');

        const fetchRelationships = currentMasto.v1.accounts.relationships.fetch(
          {
            id: [currentID],
          },
        );

        try {
          const relationships = await fetchRelationships;
          console.log('fetched relationship', relationships);
          setRelationshipUIState('default');

          if (relationships.length) {
            const relationship = relationships[0];
            setRelationship(relationship);
            onRelationshipChange({ relationship, currentID });
          }
        } catch (e) {
          console.error(e);
          setRelationshipUIState('error');
        }
      })();
    }
  }, [info, authenticated]);

  useEffect(() => {
    if (info && isSelf) {
      updateAccount(info);
    }
  }, [info, isSelf]);

  const loading = relationshipUIState === 'loading';

  const [showTranslatedBio, setShowTranslatedBio] = useState(false);
  const [showAddRemoveLists, setShowAddRemoveLists] = useState(false);
  const [showPrivateNoteModal, setShowPrivateNoteModal] = useState(false);
  const [showEditProfile, setShowEditProfile] = useState(false);
  const [lists, setLists] = useState([]);

  return (
    <>
      <div class="actions">
        <span>
          {followedBy ? (
            <span class="tag">
              <Trans>Follows you</Trans>
            </span>
          ) : !!lastStatusAt ? (
            <small class="insignificant">
              <Trans>
                Last post:{' '}
                <span class="ib">
                  {niceDateTime(lastStatusAt, {
                    hideTime: true,
                  })}
                </span>
              </Trans>
            </small>
          ) : (
            <span />
          )}
          {muting && (
            <span class="tag danger">
              <Trans>Muted</Trans>
            </span>
          )}
          {blocking && (
            <span class="tag danger">
              <Trans>Blocked</Trans>
            </span>
          )}
        </span>{' '}
        <span class="buttons">
          {!!privateNote && (
            <button
              type="button"
              class="private-note-tag"
              title={t`Private note`}
              onClick={() => {
                setShowPrivateNoteModal(true);
              }}
              dir="auto"
            >
              <span>{privateNote}</span>
            </button>
          )}
          <Menu2
            portal={{
              target: document.body,
            }}
            containerProps={{
              style: {
                // Higher than the backdrop
                zIndex: 1001,
              },
            }}
            align="center"
            position="anchor"
            overflow="auto"
            menuButton={
              <button type="button" class="plain" disabled={loading}>
                <Icon icon="more" size="l" alt={t`More`} />
              </button>
            }
            onMenuChange={(e) => {
              if (following && e.open) {
                // Fetch lists that have this account
                (async () => {
                  try {
                    const lists = await currentMasto.v1.accounts
                      .$select(accountID.current)
                      .lists.list();
                    console.log('fetched account lists', lists);
                    setLists(lists);
                  } catch (e) {
                    console.error(e);
                  }
                })();
              }
            }}
          >
            {currentAuthenticated && !isSelf && (
              <>
                <MenuItem
                  onClick={() => {
                    showCompose({
                      draftStatus: {
                        status: `@${currentInfo?.acct || acct} `,
                      },
                    });
                  }}
                >
                  <Icon icon="at" />
                  <span>
                    <Trans>
                      Mention <span class="bidi-isolate">@{username}</span>
                    </Trans>
                  </span>
                </MenuItem>
                <MenuItem
                  onClick={() => {
                    setShowTranslatedBio(true);
                  }}
                >
                  <Icon icon="translate" />
                  <span>
                    <Trans>Translate bio</Trans>
                  </span>
                </MenuItem>
                {supports('@mastodon/profile-private-note') && (
                  <MenuItem
                    onClick={() => {
                      setShowPrivateNoteModal(true);
                    }}
                  >
                    <Icon icon="pencil" />
                    <span>
                      {privateNote ? t`Edit private note` : t`Add private note`}
                    </span>
                  </MenuItem>
                )}
                {following && !!relationship && (
                  <>
                    <MenuItem
                      onClick={() => {
                        setRelationshipUIState('loading');
                        (async () => {
                          try {
                            const rel = await currentMasto.v1.accounts
                              .$select(accountID.current)
                              .follow({
                                notify: !notifying,
                              });
                            if (rel) setRelationship(rel);
                            setRelationshipUIState('default');
                            showToast(
                              rel.notifying
                                ? t`Notifications enabled for @${username}'s posts.`
                                : t` Notifications disabled for @${username}'s posts.`,
                            );
                          } catch (e) {
                            alert(e);
                            setRelationshipUIState('error');
                          }
                        })();
                      }}
                    >
                      <Icon icon="notification" />
                      <span>
                        {notifying
                          ? t`Disable notifications`
                          : t`Enable notifications`}
                      </span>
                    </MenuItem>
                    <MenuItem
                      onClick={() => {
                        setRelationshipUIState('loading');
                        (async () => {
                          try {
                            const rel = await currentMasto.v1.accounts
                              .$select(accountID.current)
                              .follow({
                                reblogs: !showingReblogs,
                              });
                            if (rel) setRelationship(rel);
                            setRelationshipUIState('default');
                            showToast(
                              rel.showingReblogs
                                ? t`Boosts from @${username} enabled.`
                                : t`Boosts from @${username} disabled.`,
                            );
                          } catch (e) {
                            alert(e);
                            setRelationshipUIState('error');
                          }
                        })();
                      }}
                    >
                      <Icon icon="rocket" />
                      <span>
                        {showingReblogs ? t`Disable boosts` : t`Enable boosts`}
                      </span>
                    </MenuItem>
                  </>
                )}
                {/* Add/remove from lists is only possible if following the account */}
                {following && (
                  <MenuItem
                    onClick={() => {
                      setShowAddRemoveLists(true);
                    }}
                  >
                    <Icon icon="list" />
                    {lists.length ? (
                      <>
                        <small class="menu-grow">
                          <Trans>Add/Remove from Lists</Trans>
                          <br />
                          <span class="more-insignificant">
                            {lists.map((list) => list.title).join(', ')}
                          </span>
                        </small>
                        <small class="more-insignificant">{lists.length}</small>
                      </>
                    ) : (
                      <span>
                        <Trans>Add/Remove from Lists</Trans>
                      </span>
                    )}
                  </MenuItem>
                )}
                <MenuDivider />
              </>
            )}
            <MenuItem
              onClick={() => {
                const handle = `@${currentInfo?.acct || acctWithInstance}`;
                try {
                  navigator.clipboard.writeText(handle);
                  showToast(t`Handle copied`);
                } catch (e) {
                  console.error(e);
                  showToast(t`Unable to copy handle`);
                }
              }}
            >
              <Icon icon="copy" />
              <small>
                <Trans>Copy handle</Trans>
                <br />
                <span class="more-insignificant bidi-isolate">
                  @{currentInfo?.acct || acctWithInstance}
                </span>
              </small>
            </MenuItem>
            <MenuItem href={url} target="_blank">
              <Icon icon="external" />
              <small class="menu-double-lines">{niceAccountURL(url)}</small>
            </MenuItem>
            <div class="menu-horizontal">
              <MenuItem
                onClick={() => {
                  // Copy url to clipboard
                  try {
                    navigator.clipboard.writeText(url);
                    showToast(t`Link copied`);
                  } catch (e) {
                    console.error(e);
                    showToast(t`Unable to copy link`);
                  }
                }}
              >
                <Icon icon="link" />
                <span>
                  <Trans>Copy</Trans>
                </span>
              </MenuItem>
              {navigator?.share &&
                navigator?.canShare?.({
                  url,
                }) && (
                  <MenuItem
                    onClick={() => {
                      try {
                        navigator.share({
                          url,
                        });
                      } catch (e) {
                        console.error(e);
                        alert(t`Sharing doesn't seem to work.`);
                      }
                    }}
                  >
                    <Icon icon="share" />
                    <span>
                      <Trans>Share…</Trans>
                    </span>
                  </MenuItem>
                )}
            </div>
            {!!relationship && (
              <>
                <MenuDivider />
                {muting ? (
                  <MenuItem
                    onClick={() => {
                      setRelationshipUIState('loading');
                      (async () => {
                        try {
                          const newRelationship = await currentMasto.v1.accounts
                            .$select(currentInfo?.id || id)
                            .unmute();
                          console.log('unmuting', newRelationship);
                          setRelationship(newRelationship);
                          setRelationshipUIState('default');
                          showToast(t`Unmuted @${username}`);
                          states.reloadGenericAccounts.id = 'mute';
                          states.reloadGenericAccounts.counter++;
                        } catch (e) {
                          console.error(e);
                          setRelationshipUIState('error');
                        }
                      })();
                    }}
                  >
                    <Icon icon="unmute" />
                    <span>
                      <Trans>
                        Unmute <span class="bidi-isolate">@{username}</span>
                      </Trans>
                    </span>
                  </MenuItem>
                ) : (
                  <SubMenu2
                    menuClassName="menu-blur"
                    openTrigger="clickOnly"
                    direction="bottom"
                    overflow="auto"
                    shift={16}
                    label={
                      <>
                        <Icon icon="mute" />
                        <span class="menu-grow">
                          <Trans>
                            Mute <span class="bidi-isolate">@{username}</span>…
                          </Trans>
                        </span>
                        <span
                          style={{
                            textOverflow: 'clip',
                          }}
                        >
                          <Icon icon="time" />
                          <Icon icon="chevron-right" />
                        </span>
                      </>
                    }
                  >
                    <div class="menu-wrap">
                      {MUTE_DURATIONS.map((duration) => (
                        <MenuItem
                          onClick={() => {
                            setRelationshipUIState('loading');
                            (async () => {
                              try {
                                const newRelationship =
                                  await currentMasto.v1.accounts
                                    .$select(currentInfo?.id || id)
                                    .mute({
                                      duration,
                                    });
                                console.log('muting', newRelationship);
                                setRelationship(newRelationship);
                                setRelationshipUIState('default');
                                showToast(
                                  t`Muted @${username} for ${
                                    typeof MUTE_DURATIONS_LABELS[duration] ===
                                    'function'
                                      ? MUTE_DURATIONS_LABELS[duration]()
                                      : _(MUTE_DURATIONS_LABELS[duration])
                                  }`,
                                );
                                states.reloadGenericAccounts.id = 'mute';
                                states.reloadGenericAccounts.counter++;
                              } catch (e) {
                                console.error(e);
                                setRelationshipUIState('error');
                                showToast(t`Unable to mute @${username}`);
                              }
                            })();
                          }}
                        >
                          {typeof MUTE_DURATIONS_LABELS[duration] === 'function'
                            ? MUTE_DURATIONS_LABELS[duration]()
                            : _(MUTE_DURATIONS_LABELS[duration])}
                        </MenuItem>
                      ))}
                    </div>
                  </SubMenu2>
                )}
                {followedBy && (
                  <MenuConfirm
                    subMenu
                    menuItemClassName="danger"
                    confirmLabel={
                      <>
                        <Icon icon="user-x" />
                        <span>
                          <Trans>
                            Remove <span class="bidi-isolate">@{username}</span>{' '}
                            from followers?
                          </Trans>
                        </span>
                      </>
                    }
                    onClick={() => {
                      setRelationshipUIState('loading');
                      (async () => {
                        try {
                          const newRelationship = await currentMasto.v1.accounts
                            .$select(currentInfo?.id || id)
                            .removeFromFollowers();
                          console.log(
                            'removing from followers',
                            newRelationship,
                          );
                          setRelationship(newRelationship);
                          setRelationshipUIState('default');
                          showToast(t`@${username} removed from followers`);
                          states.reloadGenericAccounts.id = 'followers';
                          states.reloadGenericAccounts.counter++;
                        } catch (e) {
                          console.error(e);
                          setRelationshipUIState('error');
                        }
                      })();
                    }}
                  >
                    <Icon icon="user-x" />
                    <span>
                      <Trans>Remove follower…</Trans>
                    </span>
                  </MenuConfirm>
                )}
                <MenuConfirm
                  subMenu
                  confirm={!blocking}
                  confirmLabel={
                    <>
                      <Icon icon="block" />
                      <span>
                        <Trans>
                          Block <span class="bidi-isolate">@{username}</span>?
                        </Trans>
                      </span>
                    </>
                  }
                  itemProps={{
                    className: 'danger',
                  }}
                  menuItemClassName="danger"
                  onClick={() => {
                    // if (!blocking && !confirm(`Block @${username}?`)) {
                    //   return;
                    // }
                    setRelationshipUIState('loading');
                    (async () => {
                      try {
                        if (blocking) {
                          const newRelationship = await currentMasto.v1.accounts
                            .$select(currentInfo?.id || id)
                            .unblock();
                          console.log('unblocking', newRelationship);
                          setRelationship(newRelationship);
                          setRelationshipUIState('default');
                          showToast(t`Unblocked @${username}`);
                        } else {
                          const newRelationship = await currentMasto.v1.accounts
                            .$select(currentInfo?.id || id)
                            .block();
                          console.log('blocking', newRelationship);
                          setRelationship(newRelationship);
                          setRelationshipUIState('default');
                          showToast(t`Blocked @${username}`);
                        }
                        states.reloadGenericAccounts.id = 'block';
                        states.reloadGenericAccounts.counter++;
                      } catch (e) {
                        console.error(e);
                        setRelationshipUIState('error');
                        if (blocking) {
                          showToast(t`Unable to unblock @${username}`);
                        } else {
                          showToast(t`Unable to block @${username}`);
                        }
                      }
                    })();
                  }}
                >
                  {blocking ? (
                    <>
                      <Icon icon="unblock" />
                      <span>
                        <Trans>
                          Unblock <span class="bidi-isolate">@{username}</span>
                        </Trans>
                      </span>
                    </>
                  ) : (
                    <>
                      <Icon icon="block" />
                      <span>
                        <Trans>
                          Block <span class="bidi-isolate">@{username}</span>…
                        </Trans>
                      </span>
                    </>
                  )}
                </MenuConfirm>
                <MenuItem
                  className="danger"
                  onClick={() => {
                    states.showReportModal = {
                      account: currentInfo || info,
                    };
                  }}
                >
                  <Icon icon="flag" />
                  <span>
                    <Trans>
                      Report <span class="bidi-isolate">@{username}</span>…
                    </Trans>
                  </span>
                </MenuItem>
              </>
            )}
            {currentAuthenticated &&
              isSelf &&
              standalone &&
              supports('@mastodon/profile-edit') && (
                <>
                  <MenuDivider />
                  <MenuItem
                    onClick={() => {
                      setShowEditProfile(true);
                    }}
                  >
                    <Icon icon="pencil" />
                    <span>
                      <Trans>Edit profile</Trans>
                    </span>
                  </MenuItem>
                </>
              )}
            {import.meta.env.DEV && currentAuthenticated && isSelf && (
              <>
                <MenuDivider />
                <MenuItem
                  onClick={async () => {
                    const relationships =
                      await currentMasto.v1.accounts.relationships.fetch({
                        id: [accountID.current],
                      });
                    const { note } = relationships[0] || {};
                    if (note) {
                      alert(note);
                      console.log(note);
                    }
                  }}
                >
                  <Icon icon="pencil" />
                  <span>See note</span>
                </MenuItem>
              </>
            )}
          </Menu2>
          {!relationship && relationshipUIState === 'loading' && (
            <Loader abrupt />
          )}
          {!!relationship && !moved && (
            <MenuConfirm
              confirm={following || requested}
              confirmLabel={
                <span>
                  {requested
                    ? t`Withdraw follow request?`
                    : t`Unfollow @${info.acct || info.username}?`}
                </span>
              }
              menuItemClassName="danger"
              align="end"
              disabled={loading}
              onClick={() => {
                setRelationshipUIState('loading');
                (async () => {
                  try {
                    let newRelationship;

                    if (following || requested) {
                      // const yes = confirm(
                      //   requested
                      //     ? 'Withdraw follow request?'
                      //     : `Unfollow @${info.acct || info.username}?`,
                      // );

                      // if (yes) {
                      newRelationship = await currentMasto.v1.accounts
                        .$select(accountID.current)
                        .unfollow();
                      // }
                    } else {
                      newRelationship = await currentMasto.v1.accounts
                        .$select(accountID.current)
                        .follow();
                    }

                    if (newRelationship) setRelationship(newRelationship);
                    setRelationshipUIState('default');
                  } catch (e) {
                    alert(e);
                    setRelationshipUIState('error');
                  }
                })();
              }}
            >
              <button
                type="button"
                class={`${following || requested ? 'light swap' : ''}`}
                data-swap-state={following || requested ? 'danger' : ''}
                disabled={loading}
              >
                {following ? (
                  <>
                    <span>
                      <Trans>Following</Trans>
                    </span>
                    <span>
                      <Trans>Unfollow…</Trans>
                    </span>
                  </>
                ) : requested ? (
                  <>
                    <span>
                      <Trans>Requested</Trans>
                    </span>
                    <span>
                      <Trans>Withdraw…</Trans>
                    </span>
                  </>
                ) : locked ? (
                  <>
                    <Icon icon="lock" />{' '}
                    <span>
                      <Trans>Follow</Trans>
                    </span>
                  </>
                ) : (
                  t`Follow`
                )}
              </button>
            </MenuConfirm>
          )}
        </span>
      </div>
      {!!showTranslatedBio && (
        <Modal
          onClose={() => {
            setShowTranslatedBio(false);
          }}
        >
          <TranslatedBioSheet
            note={note}
            fields={fields}
            onClose={() => setShowTranslatedBio(false)}
          />
        </Modal>
      )}
      {!!showAddRemoveLists && (
        <Modal
          onClose={() => {
            setShowAddRemoveLists(false);
          }}
        >
          <AddRemoveListsSheet
            accountID={accountID.current}
            onClose={() => setShowAddRemoveLists(false)}
          />
        </Modal>
      )}
      {!!showPrivateNoteModal && (
        <Modal
          onClose={() => {
            setShowPrivateNoteModal(false);
          }}
        >
          <PrivateNoteSheet
            account={info}
            note={privateNote}
            onRelationshipChange={(relationship) => {
              setRelationship(relationship);
              // onRelationshipChange({ relationship, currentID: accountID.current });
            }}
            onClose={() => setShowPrivateNoteModal(false)}
          />
        </Modal>
      )}
      {!!showEditProfile && (
        <Modal
          onClose={() => {
            setShowEditProfile(false);
          }}
        >
          <EditProfileSheet
            onClose={({ state, account } = {}) => {
              setShowEditProfile(false);
              if (state === 'success' && account) {
                onProfileUpdate(account);
              }
            }}
          />
        </Modal>
      )}
    </>
  );
}

// Apply more alpha if high luminence
function lightenRGB([r, g, b]) {
  const luminence = 0.2126 * r + 0.7152 * g + 0.0722 * b;
  console.log('luminence', luminence);
  let alpha;
  if (luminence >= 220) {
    alpha = 1;
  } else if (luminence <= 50) {
    alpha = 0.1;
  } else {
    alpha = luminence / 255;
  }
  alpha = Math.min(1, alpha);
  return [r, g, b, alpha];
}

function niceAccountURL(url) {
  if (!url) return;
  const urlObj = URL.parse(url);
  const { host, pathname } = urlObj;
  const path = pathname.replace(/\/$/, '').replace(/^\//, '');
  return (
    <>
      <span class="more-insignificant">{punycode.toUnicode(host)}/</span>
      <wbr />
      <span>{path}</span>
    </>
  );
}

function TranslatedBioSheet({ note, fields, onClose }) {
  const fieldsText =
    fields
      ?.map(({ name, value }) => `${name}\n${getHTMLText(value)}`)
      .join('\n\n') || '';

  const text = getHTMLText(note) + (fieldsText ? `\n\n${fieldsText}` : '');

  return (
    <div class="sheet">
      {!!onClose && (
        <button type="button" class="sheet-close" onClick={onClose}>
          <Icon icon="x" alt={t`Close`} />
        </button>
      )}
      <header>
        <h2>
          <Trans>Translated Bio</Trans>
        </h2>
      </header>
      <main>
        <p
          style={{
            whiteSpace: 'pre-wrap',
          }}
        >
          {text}
        </p>
        <TranslationBlock forceTranslate text={text} />
      </main>
    </div>
  );
}

function AddRemoveListsSheet({ accountID, onClose }) {
  const { masto } = api();
  const [uiState, setUIState] = useState('default');
  const [lists, setLists] = useState([]);
  const [listsContainingAccount, setListsContainingAccount] = useState([]);
  const [reloadCount, reload] = useReducer((c) => c + 1, 0);

  useEffect(() => {
    setUIState('loading');
    (async () => {
      try {
        const lists = await getLists();
        setLists(lists);
        const listsContainingAccount = await masto.v1.accounts
          .$select(accountID)
          .lists.list();
        console.log({ lists, listsContainingAccount });
        setListsContainingAccount(listsContainingAccount);
        setUIState('default');
      } catch (e) {
        console.error(e);
        setUIState('error');
      }
    })();
  }, [reloadCount]);

  const [showListAddEditModal, setShowListAddEditModal] = useState(false);

  return (
    <div class="sheet" id="list-add-remove-container">
      {!!onClose && (
        <button type="button" class="sheet-close" onClick={onClose}>
          <Icon icon="x" alt={t`Close`} />
        </button>
      )}
      <header>
        <h2>
          <Trans>Add/Remove from Lists</Trans>
        </h2>
      </header>
      <main>
        {lists.length > 0 ? (
          <ul class="list-add-remove">
            {lists.map((list) => {
              const inList = listsContainingAccount.some(
                (l) => l.id === list.id,
              );
              return (
                <li>
                  <button
                    type="button"
                    class={`light ${inList ? 'checked' : ''}`}
                    disabled={uiState === 'loading'}
                    onClick={() => {
                      setUIState('loading');
                      (async () => {
                        try {
                          if (inList) {
                            await masto.v1.lists
                              .$select(list.id)
                              .accounts.remove({
                                accountIds: [accountID],
                              });
                          } else {
                            await masto.v1.lists
                              .$select(list.id)
                              .accounts.create({
                                accountIds: [accountID],
                              });
                          }
                          // setUIState('default');
                          reload();
                        } catch (e) {
                          console.error(e);
                          setUIState('error');
                          alert(
                            inList
                              ? t`Unable to remove from list.`
                              : t`Unable to add to list.`,
                          );
                        }
                      })();
                    }}
                  >
                    <Icon icon="check-circle" alt="☑️" />
                    <span>{list.title}</span>
                  </button>
                </li>
              );
            })}
          </ul>
        ) : uiState === 'loading' ? (
          <p class="ui-state">
            <Loader abrupt />
          </p>
        ) : uiState === 'error' ? (
          <p class="ui-state">
            <Trans>Unable to load lists.</Trans>
          </p>
        ) : (
          <p class="ui-state">
            <Trans>No lists.</Trans>
          </p>
        )}
        <button
          type="button"
          class="plain2"
          onClick={() => setShowListAddEditModal(true)}
          disabled={uiState !== 'default'}
        >
          <Icon icon="plus" size="l" />{' '}
          <span>
            <Trans>New list</Trans>
          </span>
        </button>
      </main>
      {showListAddEditModal && (
        <Modal
          onClick={(e) => {
            if (e.target === e.currentTarget) {
              setShowListAddEditModal(false);
            }
          }}
        >
          <ListAddEdit
            list={showListAddEditModal?.list}
            onClose={(result) => {
              if (result.state === 'success') {
                reload();
              }
              setShowListAddEditModal(false);
            }}
          />
        </Modal>
      )}
    </div>
  );
}

function PrivateNoteSheet({
  account,
  note: initialNote,
  onRelationshipChange = () => {},
  onClose = () => {},
}) {
  const { masto } = api();
  const [uiState, setUIState] = useState('default');
  const textareaRef = useRef(null);

  useEffect(() => {
    let timer;
    if (textareaRef.current && !initialNote) {
      timer = setTimeout(() => {
        textareaRef.current.focus?.();
      }, 100);
    }
    return () => {
      clearTimeout(timer);
    };
  }, []);

  return (
    <div class="sheet" id="private-note-container">
      {!!onClose && (
        <button type="button" class="sheet-close" onClick={onClose}>
          <Icon icon="x" alt={t`Close`} />
        </button>
      )}
      <header>
        <b>
          <Trans>
            Private note about{' '}
            <span class="bidi-isolate">
              @{account?.username || account?.acct}
            </span>
          </Trans>
        </b>
      </header>
      <main>
        <form
          onSubmit={(e) => {
            e.preventDefault();
            const formData = new FormData(e.target);
            const note = formData.get('note');
            if (note?.trim() !== initialNote?.trim()) {
              setUIState('loading');
              (async () => {
                try {
                  const newRelationship = await masto.v1.accounts
                    .$select(account?.id)
                    .note.create({
                      comment: note,
                    });
                  console.log('updated relationship', newRelationship);
                  setUIState('default');
                  onRelationshipChange(newRelationship);
                  onClose();
                } catch (e) {
                  console.error(e);
                  setUIState('error');
                  alert(e?.message || t`Unable to update private note.`);
                }
              })();
            }
          }}
        >
          <textarea
            ref={textareaRef}
            name="note"
            disabled={uiState === 'loading'}
            dir="auto"
          >
            {initialNote}
          </textarea>
          <footer>
            <button
              type="button"
              class="light"
              disabled={uiState === 'loading'}
              onClick={() => {
                onClose?.();
              }}
            >
              <Trans>Cancel</Trans>
            </button>
            <span>
              <Loader abrupt hidden={uiState !== 'loading'} />
              <button disabled={uiState === 'loading'} type="submit">
                <Trans>Save &amp; close</Trans>
              </button>
            </span>
          </footer>
        </form>
      </main>
    </div>
  );
}

function EditProfileSheet({ onClose = () => {} }) {
  const { masto } = api();
  const [uiState, setUIState] = useState('loading');
  const [account, setAccount] = useState(null);

  useEffect(() => {
    (async () => {
      try {
        const acc = await masto.v1.accounts.verifyCredentials();
        setAccount(acc);
        setUIState('default');
      } catch (e) {
        console.error(e);
        setUIState('error');
      }
    })();
  }, []);

  console.log('EditProfileSheet', account);
  const { displayName, source } = account || {};
  const { note, fields } = source || {};
  const fieldsAttributesRef = useRef(null);

  return (
    <div class="sheet" id="edit-profile-container">
      {!!onClose && (
        <button type="button" class="sheet-close" onClick={onClose}>
          <Icon icon="x" alt={t`Close`} />
        </button>
      )}
      <header>
        <b>
          <Trans>Edit profile</Trans>
        </b>
      </header>
      <main>
        {uiState === 'loading' ? (
          <p class="ui-state">
            <Loader abrupt />
          </p>
        ) : (
          <form
            onSubmit={(e) => {
              e.preventDefault();
              const formData = new FormData(e.target);
              const displayName = formData.get('display_name');
              const note = formData.get('note');
              const fieldsAttributesFields =
                fieldsAttributesRef.current.querySelectorAll(
                  'input[name^="fields_attributes"]',
                );
              const fieldsAttributes = [];
              fieldsAttributesFields.forEach((field) => {
                const name = field.name;
                const [_, index, key] =
                  name.match(/fields_attributes\[(\d+)\]\[(.+)\]/) || [];
                const value = field.value ? field.value.trim() : '';
                if (index && key && value) {
                  if (!fieldsAttributes[index]) fieldsAttributes[index] = {};
                  fieldsAttributes[index][key] = value;
                }
              });
              // Fill in the blanks
              fieldsAttributes.forEach((field) => {
                if (field.name && !field.value) {
                  field.value = '';
                }
              });

              (async () => {
                try {
                  const newAccount = await masto.v1.accounts.updateCredentials({
                    displayName,
                    note,
                    fieldsAttributes,
                  });
                  console.log('updated account', newAccount);
                  onClose?.({
                    state: 'success',
                    account: newAccount,
                  });
                } catch (e) {
                  console.error(e);
                  alert(e?.message || t`Unable to update profile.`);
                }
              })();
            }}
          >
            <p>
              <label>
                <Trans>Name</Trans>{' '}
                <input
                  type="text"
                  name="display_name"
                  defaultValue={displayName}
                  maxLength={30}
                  disabled={uiState === 'loading'}
                  dir="auto"
                />
              </label>
            </p>
            <p>
              <label>
                <Trans>Bio</Trans>
                <textarea
                  defaultValue={note}
                  name="note"
                  maxLength={500}
                  rows="5"
                  disabled={uiState === 'loading'}
                  dir="auto"
                />
              </label>
            </p>
            {/* Table for fields; name and values are in fields, min 4 rows */}
            <p>
              <Trans>Extra fields</Trans>
            </p>
            <table ref={fieldsAttributesRef}>
              <thead>
                <tr>
                  <th>
                    <Trans>Label</Trans>
                  </th>
                  <th>
                    <Trans>Content</Trans>
                  </th>
                </tr>
              </thead>
              <tbody>
                {Array.from({ length: Math.max(4, fields.length) }).map(
                  (_, i) => {
                    const { name = '', value = '' } = fields[i] || {};
                    return (
                      <FieldsAttributesRow
                        key={i}
                        name={name}
                        value={value}
                        index={i}
                        disabled={uiState === 'loading'}
                      />
                    );
                  },
                )}
              </tbody>
            </table>
            <footer>
              <button
                type="button"
                class="light"
                disabled={uiState === 'loading'}
                onClick={() => {
                  onClose?.();
                }}
              >
                <Trans>Cancel</Trans>
              </button>
              <button type="submit" disabled={uiState === 'loading'}>
                <Trans>Save</Trans>
              </button>
            </footer>
          </form>
        )}
      </main>
    </div>
  );
}

function FieldsAttributesRow({ name, value, disabled, index: i }) {
  const [hasValue, setHasValue] = useState(!!value);
  return (
    <tr>
      <td>
        <input
          type="text"
          name={`fields_attributes[${i}][name]`}
          defaultValue={name}
          disabled={disabled}
          maxLength={255}
          required={hasValue}
          dir="auto"
        />
      </td>
      <td>
        <input
          type="text"
          name={`fields_attributes[${i}][value]`}
          defaultValue={value}
          disabled={disabled}
          maxLength={255}
          onChange={(e) => setHasValue(!!e.currentTarget.value)}
          dir="auto"
        />
      </td>
    </tr>
  );
}

function AccountHandleInfo({ acct, instance }) {
  // acct = username or username@server
  let [username, server] = acct.split('@');
  if (!server) server = instance;
  return (
    <div class="handle-info">
      <span class="handle-handle">
        <b class="handle-username">{username}</b>
        <span class="handle-at">@</span>
        <b class="handle-server">{server}</b>
      </span>
      <div class="handle-legend">
        <span class="ib">
          <span class="handle-legend-icon username" /> <Trans>username</Trans>
        </span>{' '}
        <span class="ib">
          <span class="handle-legend-icon server" />{' '}
          <Trans>server domain name</Trans>
        </span>
      </div>
    </div>
  );
}

export default AccountInfo;