import { Link } from '@tanstack/react-router';
import clsx from 'clsx';
import { sanitize } from 'isomorphic-dompurify';
import {
  type MediaAttachment,
  type PreviewCard as CardEntity,
  mediaAttachmentSchema,
} from 'pl-api';
import React, { useState, useEffect } from 'react';
import { FormattedMessage } from 'react-intl';
import * as v from 'valibot';

import Blurhash from '@/components/blurhash';
import HStack from '@/components/ui/hstack';
import Icon from '@/components/ui/icon';
import Stack from '@/components/ui/stack';
import Text from '@/components/ui/text';
import Emojify from '@/features/emoji/emojify';
import { useSettings } from '@/stores/settings';
import { getTextDirection } from '@/utils/rtl';
import Purify from '@/utils/url-purify';

import HoverAccountWrapper from './hover-account-wrapper';
import Avatar from './ui/avatar';

const domParser = new DOMParser();

const handleIframeUrl = (html: string, url: string, providerName: string) => {
  const document = domParser.parseFromString(html, 'text/html').documentElement;
  const iframe = document.querySelector('iframe');
  const startTime = new URL(url).searchParams.get('t');

  if (iframe) {
    const iframeUrl = new URL(iframe.src);

    iframeUrl.searchParams.set('autoplay', '1');
    iframeUrl.searchParams.set('auto_play', '1');

    if (providerName === 'YouTube') {
      iframeUrl.searchParams.set('start', startTime ?? '');
      iframe.referrerPolicy = 'strict-origin-when-cross-origin';
    }

    iframe.allow = 'autoplay';

    iframe.src = iframeUrl.href;

    return iframe.outerHTML;
  }

  return '';
};

const getRatio = (card: CardEntity): number => {
  const ratio = card.width / card.height || 16 / 9;

  // Constrain to a sane limit
  // https://en.wikipedia.org/wiki/Aspect_ratio_(image)
  return Math.min(Math.max(9 / 16, ratio), 4);
};

interface IPreviewCardVideo {
  card: CardEntity;
}

const PreviewCardVideo: React.FC<IPreviewCardVideo> = React.memo(
  React.forwardRef<HTMLDivElement, IPreviewCardVideo>(({ card }, ref) => {
    const html = sanitize(handleIframeUrl(card.html, card.url, card.provider_name), {
      ADD_TAGS: ['iframe'],
      ADD_ATTR: ['allow', 'allowfullscreen', 'referrerpolicy'],
    });
    const content = { __html: html };

    const ratio = getRatio(card);

    return (
      <div
        ref={ref}
        className='status-card__image status-card-video'
        dangerouslySetInnerHTML={content}
        style={{ aspectRatio: ratio }}
      />
    );
  }),
);

/** Props for `PreviewCard`. */
interface IPreviewCard {
  card: CardEntity;
  maxTitle?: number;
  maxDescription?: number;
  onOpenMedia: (attachments: Array<MediaAttachment>, index: number) => void;
  compact?: boolean;
  defaultWidth?: number;
  cacheWidth?: (width: number) => void;
  horizontal?: boolean;
}

/** Displays a Mastodon link preview. Similar to OEmbed. */
const PreviewCard: React.FC<IPreviewCard> = ({
  card,
  defaultWidth = 467,
  maxTitle = 120,
  maxDescription = 200,
  compact = false,
  cacheWidth,
  onOpenMedia,
}): React.JSX.Element => {
  const {
    urlPrivacy: { clearLinksInContent, redirectLinksMode },
  } = useSettings();
  const [width, setWidth] = useState(defaultWidth);
  const [embedded, setEmbedded] = useState(false);

  useEffect(() => {
    setEmbedded(false);
  }, [card.url]);

  let href = card.url;

  if (clearLinksInContent) {
    try {
      href = Purify.clearUrl(href, clearLinksInContent, redirectLinksMode !== 'off');
    } catch (_) {
      //
    }
  }
  const direction = getTextDirection(card.title + card.description);

  const trimmedTitle = trim(card.title, maxTitle);
  const trimmedDescription = trim(card.description, maxDescription);

  const handlePhotoClick = () => {
    const attachment = v.parse(mediaAttachmentSchema, {
      id: '',
      type: 'image',
      url: card.embed_url,
      description: trimmedTitle,
      meta: {
        original: {
          width: card.width,
          height: card.height,
        },
      },
    });

    onOpenMedia([attachment], 0);
  };

  const handleEmbedClick: React.MouseEventHandler = (e) => {
    e.stopPropagation();

    if (card.type === 'photo') {
      handlePhotoClick();
    } else {
      setEmbedded(true);
    }
  };

  const setRef: React.RefCallback<HTMLElement> = (c) => {
    if (c) {
      if (cacheWidth) {
        cacheWidth(c.offsetWidth);
      }

      setWidth(c.offsetWidth);
    }
  };

  const interactive = card.type !== 'link';
  const horizontal = interactive || embedded;
  const className = clsx(
    'status-card relative z-[1] flex-col bg-white black:bg-black dark:bg-primary-900 md:flex-row',
    { horizontal, compact, interactive },
    `status-card--${card.type}`,
  );
  const ratio = getRatio(card);
  const height = compact && !embedded ? width / (16 / 9) : width / ratio;

  const title = interactive ? (
    <a
      onClick={(e) => {
        e.stopPropagation();
      }}
      href={href}
      title={trimmedTitle}
      rel='noopener'
      target='_blank'
      dir={direction}
    >
      <span dir={direction}>{trimmedTitle}</span>
    </a>
  ) : (
    <span title={trimmedTitle} dir={direction}>
      {trimmedTitle}
    </span>
  );

  const description = (
    <Stack space={2} className='flex-1 overflow-hidden p-4'>
      {trimmedTitle && (
        <Text weight='bold' direction={direction}>
          {title}
        </Text>
      )}
      {trimmedDescription && <Text direction={direction}>{trimmedDescription}</Text>}
      <HStack space={1} alignItems='center'>
        <Text tag='span' theme='muted'>
          <Icon src={require('@phosphor-icons/core/regular/link-simple.svg')} />
        </Text>
        <Text tag='span' theme='muted' size='sm' direction={direction}>
          {card.provider_name}
        </Text>
      </HStack>
    </Stack>
  );

  let embed: React.ReactNode = null;

  const canvas = <Blurhash className='absolute inset-0 -z-10 size-full' hash={card.blurhash} />;

  const thumbnail = (
    <div
      style={{
        backgroundImage: `url(${card.image})`,
        width: horizontal ? width : undefined,
        height: horizontal ? height : undefined,
        aspectRatio: ratio,
      }}
      className='status-card__image-image'
      title={card.image_description || undefined}
    />
  );

  if (interactive) {
    if (embedded) {
      embed = <PreviewCardVideo card={card} />;
    } else {
      let iconVariant = require('@phosphor-icons/core/regular/play.svg');

      if (card.type === 'photo') {
        iconVariant = require('@phosphor-icons/core/regular/magnifying-glass-plus.svg');
      }

      embed = (
        <div className='status-card__image'>
          {canvas}
          {thumbnail}

          <div className='absolute inset-0 flex items-center justify-center'>
            <div className='flex items-center justify-center rounded-full bg-gray-500/90 px-4 py-3 shadow-md dark:bg-gray-700/90'>
              <HStack space={3} alignItems='center'>
                <button
                  onClick={handleEmbedClick}
                  className='appearance-none text-gray-700 hover:text-gray-900 dark:text-gray-200 dark:hover:text-gray-100'
                >
                  <Icon src={iconVariant} className='size-6 text-inherit' />
                </button>

                {horizontal && (
                  <a
                    onClick={(e) => {
                      e.stopPropagation();
                    }}
                    href={href}
                    target='_blank'
                    rel='noopener'
                    className='text-gray-700 hover:text-gray-900 dark:text-gray-200 dark:hover:text-gray-100'
                  >
                    <Icon
                      src={require('@phosphor-icons/core/regular/arrow-square-out.svg')}
                      className='size-6 text-inherit'
                    />
                  </a>
                )}
              </HStack>
            </div>
          </div>
        </div>
      );
    }

    return (
      <div className={className} ref={setRef}>
        {embed}
        {description}
      </div>
    );
  } else if (card.image) {
    embed = (
      <div
        className={clsx(
          'status-card__image',
          'w-full flex-none rounded-l md:size-auto md:flex-auto',
          {
            'h-auto': horizontal,
            'h-[200px]': !horizontal,
          },
        )}
      >
        {canvas}
        {thumbnail}
      </div>
    );
  }

  const link = (
    <a
      href={href}
      className={className}
      target='_blank'
      rel='noopener'
      ref={setRef}
      onClick={(e) => {
        e.stopPropagation();
      }}
    >
      {embed}
      {description}
    </a>
  );

  if (card.authors.length) {
    return (
      <Stack>
        {link}
        <div className='-mt-4 rounded-lg border border-t-0 border-solid border-gray-200 bg-gray-100 p-2 pt-6 black:bg-primary-900 dark:border-gray-800 dark:bg-primary-700'>
          <Text theme='muted' className='flex items-center gap-2'>
            <FormattedMessage
              id='link_preview.more_from_author'
              defaultMessage='From {name}'
              values={{
                name: card.authors.map((author) => {
                  const linkBody = (
                    <HStack space={1} alignItems='center'>
                      {author.account && (
                        <Avatar
                          src={author.account?.avatar}
                          size={16}
                          username={author.account.username}
                        />
                      )}
                      <Text weight='medium'>
                        <Emojify
                          text={author.account?.display_name ?? author.name}
                          emojis={author.account?.emojis}
                        />
                      </Text>
                    </HStack>
                  );
                  return (
                    <HoverAccountWrapper
                      key={author.url}
                      accountId={author.account?.id}
                      element='bdi'
                    >
                      {author.account ? (
                        <Link to='/@{$username}' params={{ username: author.account?.acct ?? '' }}>
                          {linkBody}
                        </Link>
                      ) : (
                        <a href={author.url} target='_blank' rel='noopener'>
                          {linkBody}
                        </a>
                      )}
                    </HoverAccountWrapper>
                  );
                }),
              }}
            />
          </Text>
        </div>
      </Stack>
    );
  }

  return link;
};

/** Trim the text, adding ellipses if it's too long. */
const trim = (text: string, len: number): string => {
  const cut = text.indexOf(' ', len);

  if (cut === -1) {
    return text;
  }

  return text.substring(0, cut) + (text.length > len ? '…' : '');
};

export { PreviewCard as default };
