import { Link } from '@tanstack/react-router';
import clsx from 'clsx';
import React, { useState } from 'react';
import { FormattedMessage } from 'react-intl';

import Blurhash from '@/components/blurhash';
import Icon from '@/components/icon';
import LoadMore from '@/components/load-more';
import MissingIndicator from '@/components/missing-indicator';
import StillImage from '@/components/still-image';
import Column from '@/components/ui/column';
import Spinner from '@/components/ui/spinner';
import { profileMediaRoute } from '@/features/ui/router';
import { type AccountGalleryAttachment, useAccountGallery } from '@/hooks/use-account-gallery';
import { isIOS } from '@/is-mobile';
import { useAccount } from '@/queries/accounts/use-account';
import { useAccountLookup } from '@/queries/accounts/use-account-lookup';
import { useModalsActions } from '@/stores/modals';
import { useSettings } from '@/stores/settings';

interface IMediaItem {
  attachment: AccountGalleryAttachment;
  onOpenMedia: (attachment: AccountGalleryAttachment) => void;
  isLast?: boolean;
}

const MediaItem: React.FC<IMediaItem> = ({ attachment, onOpenMedia, isLast }) => {
  const { autoPlayGif, displayMedia } = useSettings();
  const { data: account } = useAccount(attachment.account_id);
  const [visible, setVisible] = useState<boolean>(
    (displayMedia !== 'hide_all' && !attachment.sensitive) || displayMedia === 'show_all',
  );

  const handleMouseEnter: React.MouseEventHandler<HTMLVideoElement> = (e) => {
    const video = e.target as HTMLVideoElement;
    if (hoverToPlay()) {
      video.play();
    }
  };

  const handleMouseLeave: React.MouseEventHandler<HTMLVideoElement> = (e) => {
    const video = e.target as HTMLVideoElement;
    if (hoverToPlay()) {
      video.pause();
      video.currentTime = 0;
    }
  };

  const hoverToPlay = () => !autoPlayGif && ['gifv', 'video'].includes(attachment.type);

  const handleClick: React.MouseEventHandler = (e) => {
    if (e.button === 0 && !(e.ctrlKey || e.metaKey)) {
      e.preventDefault();

      if (visible) {
        onOpenMedia(attachment);
      } else {
        setVisible(true);
      }
    }
  };

  const title = attachment.description;

  let thumbnail: React.ReactNode = '';
  let icon;

  if (attachment.type === 'unknown') {
    // Skip
  } else if (attachment.type === 'image') {
    const focusX = Number(attachment.meta?.focus?.x) || 0;
    const focusY = Number(attachment.meta?.focus?.y) || 0;
    const x = (focusX / 2 + 0.5) * 100;
    const y = (focusY / -2 + 0.5) * 100;

    thumbnail = (
      <StillImage
        src={attachment.preview_url}
        alt={attachment.description}
        style={{ objectPosition: `${x}% ${y}%` }}
        className={clsx('size-full overflow-hidden', { 'rounded-br-md': isLast })}
      />
    );
  } else if (['gifv', 'video'].includes(attachment.type)) {
    const conditionalAttributes: React.VideoHTMLAttributes<HTMLVideoElement> = {};
    if (isIOS()) {
      conditionalAttributes.playsInline = true;
    }
    if (autoPlayGif) {
      conditionalAttributes.autoPlay = true;
    }
    thumbnail = (
      <div className={clsx('⁂-media-gallery__gifv', { autoplay: autoPlayGif })}>
        <video
          className={clsx('⁂-media-gallery__item-gifv-thumbnail overflow-hidden', {
            'rounded-br-md': isLast,
          })}
          aria-label={attachment.description}
          title={attachment.description}
          role='application'
          src={attachment.url}
          onMouseEnter={handleMouseEnter}
          onMouseLeave={handleMouseLeave}
          loop
          muted
          {...conditionalAttributes}
        />

        <span className='⁂-media-gallery__gifv__label'>GIF</span>
      </div>
    );
  } else if (attachment.type === 'audio') {
    const remoteURL = attachment.remote_url ?? '';
    const fileExtensionLastIndex = remoteURL.lastIndexOf('.');
    const fileExtension = remoteURL.slice(fileExtensionLastIndex + 1).toUpperCase();
    thumbnail = (
      <div className={clsx('⁂-media-gallery__item-thumbnail', { 'rounded-br-md': isLast })}>
        <span className='⁂-media-gallery__item__icons'>
          <Icon src={require('@phosphor-icons/core/regular/speaker-high.svg')} />
        </span>
        <span className='⁂-media-gallery__file-extension__label'>{fileExtension}</span>
      </div>
    );
  }

  if (!visible) {
    icon = (
      <span className='⁂-media-gallery__item__icons'>
        <Icon src={require('@phosphor-icons/core/regular/eye-slash.svg')} />
      </span>
    );
  }

  return (
    <div className='col-span-1'>
      <Link
        className='⁂-media-gallery__item-thumbnail aspect-1'
        to='/@{$username}/posts/$statusId'
        params={{ username: account?.acct ?? 'undefined', statusId: attachment.status_id }}
        onClick={handleClick}
        title={title}
      >
        <Blurhash
          hash={attachment.blurhash}
          className={clsx('⁂-media-gallery__preview', {
            hidden: visible,
            'rounded-br-md': isLast,
          })}
          aria-label={!visible ? attachment.description : undefined}
          aria-hidden={visible}
        />
        {visible && thumbnail}
        {!visible && icon}
      </Link>
    </div>
  );
};

const AccountGalleryPage = () => {
  const { username } = profileMediaRoute.useParams();
  const { openModal } = useModalsActions();

  const { data: account, isLoading: accountLoading, isUnavailable } = useAccountLookup(username);

  const {
    data: attachments,
    isFetching,
    isLoading,
    hasNextPage: hasMore,
    fetchNextPage,
  } = useAccountGallery(account?.id!);

  const handleScrollToBottom = () => {
    if (hasMore) {
      handleLoadMore();
    }
  };

  const handleLoadMore = () => {
    fetchNextPage({ cancelRefetch: false });
  };

  const handleLoadOlder: React.MouseEventHandler = (e) => {
    e.preventDefault();
    handleScrollToBottom();
  };

  const handleOpenMedia = (attachment: AccountGalleryAttachment) => {
    openModal('MEDIA', { index: attachment.index, statusId: attachment.status_id });
  };

  if (accountLoading || isLoading) {
    return (
      <Column>
        <Spinner />
      </Column>
    );
  }

  if (!account) {
    return <MissingIndicator />;
  }

  let loadOlder = null;

  if (hasMore && !(isFetching && attachments.length === 0)) {
    loadOlder = (
      <LoadMore className='my-auto mt-4' visible={!isFetching} onClick={handleLoadOlder} />
    );
  }

  if (isUnavailable) {
    return (
      <Column>
        <div className='empty-column-indicator'>
          <FormattedMessage
            id='empty_column.account_unavailable'
            defaultMessage='Profile unavailable'
          />
        </div>
      </Column>
    );
  }

  return (
    <Column label={`@${account.acct}`} transparent withHeader={false}>
      <div role='feed' className='grid grid-cols-2 gap-1 overflow-hidden rounded-md sm:grid-cols-3'>
        {attachments.map((attachment, index) => (
          <MediaItem
            key={`${attachment.status_id}+${attachment.id}`}
            attachment={attachment}
            onOpenMedia={handleOpenMedia}
            isLast={index === attachments.length - 1}
          />
        ))}

        {!isLoading && attachments.length === 0 && (
          <div className='empty-column-indicator col-span-2 sm:col-span-3'>
            <FormattedMessage id='account_gallery.none' defaultMessage='No media to show.' />
          </div>
        )}
      </div>

      {loadOlder}

      {isFetching && attachments.length === 0 && (
        <div className='relative flex-auto px-8 py-4'>
          <Spinner />
        </div>
      )}
    </Column>
  );
};

export { AccountGalleryPage as default, MediaItem };
