import { useCallback, useEffect, useRef, useState } from 'react';

import { defineMessages, FormattedMessage, useIntl } from 'react-intl';

import { useHistory, useLocation, useParams } from 'react-router';
import { Link } from 'react-router-dom';

import { Helmet } from '@unhead/react/helmet';

import { Column } from '@/mastodon/components/column';
import { ColumnHeader } from '@/mastodon/components/column/header';
import HelpIcon from '@/material-icons/400-24px/help.svg?react';
import ListAltIcon from '@/material-icons/400-24px/list_alt.svg?react';
import ShareIcon from '@/material-icons/400-24px/share.svg?react';
import StarIcon from '@/material-icons/400-24px/star.svg?react';
import { openModal } from 'mastodon/actions/modal';
import type {
  ApiCollectionJSON,
  CollectionAccountItem,
} from 'mastodon/api_types/collections';
import { Badge } from 'mastodon/components/badge';
import { Callout } from 'mastodon/components/callout';
import { DisplayName } from 'mastodon/components/display_name';
import { useAccountHandle } from 'mastodon/components/display_name/default';
import { FormattedDateWrapper } from 'mastodon/components/formatted_date';
import { IconButton } from 'mastodon/components/icon_button';
import { LoadingIndicator } from 'mastodon/components/loading_indicator';
import { Scrollable } from 'mastodon/components/scrollable_list/components';
import { useAccount } from 'mastodon/hooks/useAccount';
import { domain, me } from 'mastodon/initial_state';
import { fetchCollection } from 'mastodon/reducers/slices/collections';
import { useAppDispatch, useAppSelector } from 'mastodon/store';

import { CollectionMenu } from '../components/collection_menu';

import { CollectionAccountsList } from './accounts_list';
import { useConfirmRevoke } from './revoke_collection_inclusion_modal';
import classes from './styles.module.scss';

const messages = defineMessages({
  loading: {
    id: 'collections.detail.loading',
    defaultMessage: 'Loading collection…',
  },
  share: {
    id: 'collections.detail.share',
    defaultMessage: 'Share this collection',
  },
});

export const AuthorNote: React.FC<{ id: string }> = ({ id }) => {
  const account = useAccount(id);
  const authorHandle = useAccountHandle(account, domain);

  if (!account) {
    return null;
  }

  const author = (
    <Link to={`/@${account.acct}`} data-hover-card-account={account.id}>
      {authorHandle}
    </Link>
  );

  return (
    <p className={classes.authorNote}>
      <FormattedMessage
        id='collections.by_account'
        defaultMessage='by {account_handle}'
        values={{
          account_handle: author,
        }}
      />
    </p>
  );
};

const RevokeControls: React.FC<{
  currentUserCollectionItem: CollectionAccountItem;
  collection: ApiCollectionJSON;
}> = ({ currentUserCollectionItem, collection }) => {
  const authorAccount = useAccount(collection.account_id);
  const confirmRevoke = useConfirmRevoke(collection);

  return (
    <Callout
      icon={StarIcon}
      title={
        <FormattedMessage
          id='collections.detail.you_are_in_this_collection'
          defaultMessage="You're featured in this collection"
        />
      }
      primaryLabel={
        <FormattedMessage
          id='collections.detail.revoke_inclusion'
          defaultMessage='Remove me'
        />
      }
      onPrimary={confirmRevoke}
    >
      <FormattedMessage
        id='collections.detail.author_added_you_on_date'
        defaultMessage='{author} added you on {date}'
        values={{
          author: <DisplayName account={authorAccount} variant='simple' />,
          date: (
            <FormattedDateWrapper
              value={currentUserCollectionItem.created_at}
              day='2-digit'
              month='short'
              year='numeric'
            />
          ),
        }}
      />
    </Callout>
  );
};

export const PendingNote: React.FC = () => {
  return (
    <Callout
      variant='subtle'
      icon={HelpIcon}
      title={
        <FormattedMessage
          id='collections.pending_accounts.title'
          defaultMessage='Why am I seeing pending accounts?'
        />
      }
    >
      <FormattedMessage
        id='collections.pending_accounts.message'
        defaultMessage='Accounts may appear as pending when we’re awaiting a response from the user or their server. Only you can see pending accounts.'
      />
    </Callout>
  );
};

const SensitiveContentNote: React.FC<{ onReveal: () => void }> = ({
  onReveal,
}) => {
  return (
    <Callout
      variant='warning'
      title={
        <FormattedMessage
          id='collections.detail.sensitive_content'
          defaultMessage='Sensitive content'
        />
      }
      primaryLabel={
        <FormattedMessage
          id='content_warning.show_short'
          defaultMessage='Show'
        />
      }
      onPrimary={onReveal}
      className={classes.sensitiveScreen}
    >
      <FormattedMessage
        id='collections.detail.sensitive_note'
        defaultMessage='The description and accounts may not be suitable for all viewers.'
      />
    </Callout>
  );
};

const CollectionHeader: React.FC<{
  collection: ApiCollectionJSON;
  withDescription: boolean;
  headingRef: React.RefObject<HTMLHeadingElement | null>;
}> = ({ collection, withDescription, headingRef }) => {
  const intl = useIntl();
  const { name, description, tag, account_id, items } = collection;
  const dispatch = useAppDispatch();
  const history = useHistory();

  const isOwnCollection = account_id === me;
  const currentUserCollectionItem = items.find(
    (account) => account.account_id === me,
  );
  const isCurrentUserInCollection =
    !isOwnCollection && !!currentUserCollectionItem;

  const openShareModal = useCallback(() => {
    dispatch(
      openModal({
        modalType: 'SHARE_COLLECTION',
        modalProps: {
          collection,
        },
      }),
    );
  }, [collection, dispatch]);

  const location = useLocation<{ newCollection?: boolean } | undefined>();
  const isNewCollection = location.state?.newCollection;
  useEffect(() => {
    if (isNewCollection) {
      // Replace with current pathname to clear `newCollection` state
      history.replace(location.pathname);
      openShareModal();
    }
  }, [history, openShareModal, isNewCollection, location.pathname]);

  const hasPendingAccounts = items.some((item) => item.state === 'pending');

  return (
    <header className={classes.header}>
      <div className={classes.titleWithMenu}>
        <div className={classes.titleWrapper}>
          {tag && <Badge label={`#${tag.name}`} icon={null} />}
          <h2 className={classes.name} ref={headingRef} tabIndex={-1}>
            {name}
          </h2>
          <AuthorNote id={account_id} />
        </div>
        <div className={classes.headerButtonWrapper}>
          <IconButton
            iconComponent={ShareIcon}
            icon='share-icon'
            title={intl.formatMessage(messages.share)}
            className={classes.iconButton}
            onClick={openShareModal}
          />
          <CollectionMenu
            context='collection'
            collection={collection}
            className={classes.iconButton}
          />
        </div>
      </div>
      {withDescription && description && (
        <p className={classes.description}>{description}</p>
      )}
      {hasPendingAccounts && <PendingNote />}
      {isCurrentUserInCollection && (
        <RevokeControls
          currentUserCollectionItem={currentUserCollectionItem}
          collection={collection}
        />
      )}
    </header>
  );
};

function useRevealSensitiveContent({
  sensitive,
}: {
  sensitive: boolean | undefined;
}) {
  const postRevealFocusTargetRef = useRef<HTMLHeadingElement>(null);
  const [isContentVisible, setIsContentVisible] = useState(!sensitive);

  const revealContent = useCallback(() => {
    setIsContentVisible(true);
    setTimeout(() => {
      postRevealFocusTargetRef.current?.focus();
    }, 0);
  }, [postRevealFocusTargetRef]);

  return {
    isContentVisible,
    revealContent,
    postRevealFocusTargetRef,
  };
}

const ColumnContent: React.FC<{
  collection: ApiCollectionJSON;
}> = ({ collection }) => {
  const { isContentVisible, revealContent, postRevealFocusTargetRef } =
    useRevealSensitiveContent({
      sensitive: collection.sensitive && collection.account_id !== me,
    });

  return (
    <>
      <CollectionHeader
        collection={collection}
        headingRef={postRevealFocusTargetRef}
        withDescription={isContentVisible}
      />
      {isContentVisible ? (
        <CollectionAccountsList collection={collection} />
      ) : (
        <SensitiveContentNote onReveal={revealContent} />
      )}
    </>
  );
};

export const CollectionDetailPage: React.FC<{
  multiColumn?: boolean;
}> = ({ multiColumn }) => {
  const intl = useIntl();
  const dispatch = useAppDispatch();
  const { id } = useParams<{ id?: string }>();
  const collection = useAppSelector((state) =>
    id ? state.collections.collections[id] : undefined,
  );

  useEffect(() => {
    if (id) {
      void dispatch(fetchCollection({ collectionId: id }));
    }
  }, [dispatch, id]);

  const pageTitle = collection?.name ?? intl.formatMessage(messages.loading);

  return (
    <Column bindToDocument={!multiColumn} label={pageTitle}>
      <ColumnHeader
        showBackButton
        title={pageTitle}
        icon='collection-icon'
        iconComponent={ListAltIcon}
        multiColumn={multiColumn}
      />

      <Scrollable>
        {collection ? (
          <ColumnContent collection={collection} />
        ) : (
          <LoadingIndicator />
        )}
      </Scrollable>

      <Helmet>
        <title>{pageTitle}</title>
        <meta name='robots' content='noindex' />
      </Helmet>
    </Column>
  );
};
