import { createPopper } from '@popperjs/core'
import { Link } from '@solidjs/meta'
import { A, useSearchParams } from '@solidjs/router'
import { clsx } from 'clsx'
import { For, Show, createEffect, createMemo, createSignal, on, onCleanup, onMount } from 'solid-js'
import { isServer } from 'solid-js/web'
import { useFeed } from '~/context/feed'
import { useLocalize } from '~/context/localize'
import { useReactions } from '~/context/reactions'
import { useSession } from '~/context/session'
import { DEFAULT_HEADER_OFFSET, useUI } from '~/context/ui'
import type { Author, Maybe, Shout, Topic } from '~/graphql/schema/core.gen'
import { processPrepositions } from '~/intl/prepositions'
import { isCyrillic } from '~/intl/translate'
import { getImageUrl } from '~/lib/getThumbUrl'
import { MediaItem } from '~/types/mediaitem'
import { capitalize } from '~/utils/capitalize'
import { AuthorBadge } from '../Author/AuthorBadge'
import { CardTopic } from '../Feed/CardTopic'
import { FeedArticlePopup } from '../Feed/FeedArticlePopup'
import stylesHeader from '../HeaderNav/Header.module.scss'
import { Icon } from '../_shared/Icon'
import { Image } from '../_shared/Image'
import { InviteMembers } from '../_shared/InviteMembers'
import { Lightbox } from '../_shared/Lightbox'
import { Modal } from '../_shared/Modal'
import { Popover } from '../_shared/Popover'
import { ShareModal } from '../_shared/ShareModal'
import { ImageSwiper } from '../_shared/SolidSwiper'
import { TableOfContents } from '../_shared/TableOfContents'
import { VideoPlayer } from '../_shared/VideoPlayer'
import styles from './Article.module.scss'
import { AudioHeader } from './AudioHeader'
import { AudioPlayer } from './AudioPlayer'
import { CommentsTree } from './CommentsTree'
import { RatingControl as ShoutRatingControl } from './RatingControl'
import { SharePopup, getShareUrl } from './SharePopup'

type Props = {
  article: Shout
  scrollToComments?: boolean
}

type IframeSize = {
  width: number
  height: number
}

export type ArticlePageSearchParams = {
  scrollTo: 'comments'
  commentId: string
  slide?: string
}

const scrollTo = (el: HTMLElement) => {
  const { top } = el.getBoundingClientRect()

  window?.scrollTo({
    top: top + window.scrollY - DEFAULT_HEADER_OFFSET,
    left: 0,
    behavior: 'smooth'
  })
}

const imgSrcRegExp = /<img[^>]+src\s*=\s*["']([^"']+)["']/gi
const COMMENTS_PER_PAGE = 30
const VOTES_PER_PAGE = 50

export const FullArticle = (props: Props) => {
  const [searchParams, changeSearchParams] = useSearchParams<ArticlePageSearchParams>()
  const { showModal } = useUI()
  const { loadReactionsBy } = useReactions()
  const [selectedImage, setSelectedImage] = createSignal('')
  const [isReactionsLoaded, setIsReactionsLoaded] = createSignal(false)
  const [isActionPopupActive, setIsActionPopupActive] = createSignal(false)
  const { t, formatDate, lang } = useLocalize()
  const { session, requireAuthentication } = useSession()
  const author = createMemo<Author>(() => session()?.user?.app_data?.profile as Author)
  const { addSeen } = useFeed()
  const formattedDate = createMemo(() => formatDate(new Date((props.article.published_at || 0) * 1000)))

  const [pages, setPages] = createSignal<Record<string, number>>({})
  createEffect(
    on(
      pages,
      async (p: Record<string, number>) => {
        await loadReactionsBy({
          by: { shout: props.article.slug, comment: true },
          limit: COMMENTS_PER_PAGE,
          offset: COMMENTS_PER_PAGE * p.comments || 0
        })
        await loadReactionsBy({
          by: { shout: props.article.slug, rating: true },
          limit: VOTES_PER_PAGE,
          offset: VOTES_PER_PAGE * p.rating || 0
        })
        setIsReactionsLoaded(true)
      },
      { defer: true }
    )
  )

  const canEdit = createMemo(
    () =>
      Boolean(author()?.id) &&
      (props.article.authors?.some((a) => Boolean(a) && a?.id === author().id) ||
        props.article.created_by?.id === author().id ||
        session()?.user?.roles?.includes('editor'))
  )

  const mainTopic = createMemo(() => {
    const mainTopicSlug = (props.article.topics?.length || 0) > 0 ? props.article.main_topic : null
    const mt = props.article.topics?.find((tpc: Maybe<Topic>) => tpc?.slug === mainTopicSlug)
    if (mt) {
      mt.title = lang() === 'en' ? capitalize(mt.slug.replace(/-/, ' ')) : mt.title
      return mt
    }
    return props.article.topics?.[0]
  })

  const handleBookmarkButtonClick = (ev: MouseEvent | undefined) => {
    requireAuthentication(() => {
      // TODO: implement bookmark clicked
      ev?.preventDefault()
    }, 'bookmark')
  }

  const body = createMemo(() => {
    if (props.article.layout === 'literature') {
      try {
        if (props.article.media) {
          const media = JSON.parse(props.article.media)
          if (media.length > 0) {
            return processPrepositions(media[0].body)
          }
        }
      } catch (error) {
        console.error(error)
      }
    }
    return processPrepositions(props.article.body) || ''
  })

  const imageUrls = createMemo(() => {
    if (!body()) {
      return []
    }

    if (isServer) {
      const result: string[] = []
      let match: RegExpMatchArray | null

      while ((match = imgSrcRegExp.exec(body())) !== null) {
        if (match) result.push(match[1])
        else break
      }
      return result
    }

    const imageElements = document.querySelectorAll<HTMLImageElement>('#shoutBody img')
    // eslint-disable-next-line unicorn/prefer-spread
    return Array.from(imageElements).map((img) => img.src)
  })

  const media = createMemo<MediaItem[]>(() => JSON.parse(props.article.media || '[]'))

  let commentsRef: HTMLDivElement | undefined

  createEffect(() => {
    if (searchParams?.commentId && isReactionsLoaded()) {
      const commentElement = document.querySelector<HTMLElement>(
        `[id='comment_${searchParams?.commentId}']`
      )

      if (commentElement) {
        requestAnimationFrame(() => scrollTo(commentElement))
      }
    }
  })

  const clickHandlers: { element: HTMLElement; handler: () => void }[] = []
  const documentClickHandlers: ((e: MouseEvent) => void)[] = []

  createEffect(() => {
    if (!body()) {
      return
    }

    const tooltipElements: NodeListOf<HTMLElement> = document.querySelectorAll(
      '[data-toggle="tooltip"], footnote'
    )
    if (!tooltipElements) {
      return
    }
    tooltipElements.forEach((element) => {
      const tooltip = document.createElement('div')
      tooltip.classList.add(styles.tooltip)
      const tooltipContent = document.createElement('div')
      tooltipContent.classList.add(styles.tooltipContent)
      tooltipContent.innerHTML = element.dataset.originalTitle || element.dataset.value || ''

      tooltip.append(tooltipContent)

      document.body.append(tooltip)

      if (element.hasAttribute('href')) {
        element.setAttribute('href', 'javascript: void(0)')
      }

      const popperInstance = createPopper(element, tooltip, {
        placement: 'top',
        modifiers: [
          {
            name: 'eventListeners',
            options: { scroll: false }
          },
          {
            name: 'offset',
            options: {
              offset: [0, 8]
            }
          },
          {
            name: 'flip',
            options: { fallbackPlacements: ['top'] }
          }
        ]
      })

      tooltip.style.visibility = 'hidden'
      let isTooltipVisible = false
      const handleClick = () => {
        if (isTooltipVisible) {
          tooltip.style.visibility = 'hidden'
          isTooltipVisible = false
        } else {
          tooltip.style.visibility = 'visible'
          isTooltipVisible = true
        }

        popperInstance.update()
      }

      const handleDocumentClick = (e: MouseEvent) => {
        if (isTooltipVisible && e.target !== element && e.target !== tooltip) {
          tooltip.style.visibility = 'hidden'
          isTooltipVisible = false
        }
      }

      element.addEventListener('click', handleClick)
      document.addEventListener('click', handleDocumentClick)

      clickHandlers.push({ element, handler: handleClick })
      documentClickHandlers.push(handleDocumentClick)
    })
  })

  onCleanup(() => {
    clickHandlers.forEach(({ element, handler }) => {
      element.removeEventListener('click', handler)
    })
    documentClickHandlers.forEach((handler) => {
      document.removeEventListener('click', handler)
    })
  })

  const openLightbox = (image: string) => {
    setSelectedImage(image)
  }
  const handleLightboxClose = () => {
    setSelectedImage('')
  }

  // biome-ignore lint/suspicious/noExplicitAny: FIXME: typing
  const handleArticleBodyClick = (event: any) => {
    if (event.target.tagName === 'IMG' && !event.target.dataset.disableLightbox) {
      const src = event.target.src
      openLightbox(getImageUrl(src))
    }
  }

  // Check iframes size
  let articleContainer: HTMLElement | undefined
  const updateIframeSizes = () => {
    if (!window) return
    if (!(articleContainer && props.article.body)) return
    const iframes = articleContainer?.querySelectorAll('iframe')
    if (!iframes) return
    const containerWidth = articleContainer?.offsetWidth
    iframes.forEach((iframe) => {
      const style = window.getComputedStyle(iframe)
      const originalWidth = iframe.getAttribute('width') || style.width.replace('px', '')
      const originalHeight = iframe.getAttribute('height') || style.height.replace('px', '')

      const width: IframeSize['width'] = Number(originalWidth)
      const height: IframeSize['height'] = Number(originalHeight)

      if (containerWidth < width) {
        const aspectRatio = width / height
        iframe.style.width = `${containerWidth}px`
        iframe.style.height = `${Math.round(containerWidth / aspectRatio) + 40}px`
      } else {
        iframe.style.height = `${containerWidth}px`
      }
    })
  }

  createEffect(
    on(
      () => props.article,
      () => {
        updateIframeSizes()
      }
    )
  )
  const [ratings, setRatings] = createSignal<Reaction[]>([])

  onMount(async () => {
    // install('G-LQ4B87H8C2')
    await loadReactionsBy({ by: { shout: props.article.slug } })
    addSeen(props.article.slug)
    document.title = props.article.title
    updateIframeSizes()
    window?.addEventListener('resize', updateIframeSizes)
    onCleanup(() => window.removeEventListener('resize', updateIframeSizes))
  })

  createEffect(() => props.scrollToComments && commentsRef && scrollTo(commentsRef))
  createEffect(() => {
    if (searchParams?.scrollTo === 'comments' && commentsRef) {
      requestAnimationFrame(() => commentsRef && scrollTo(commentsRef))
      changeSearchParams({ scrollTo: undefined })
    }
  })

  const shareUrl = createMemo(() => getShareUrl({ pathname: `/${props.article.slug || ''}` }))
  const getAuthorName = (a: Author) =>
    lang() === 'en' && isCyrillic(a.name || '') ? capitalize(a.slug.replace(/-/, ' ')) : a.name
  return (
    <>
      <For each={imageUrls()}>{(imageUrl) => <Link rel="preload" as="image" href={imageUrl} />}</For>
      <div class="wide-container">
        <div class="row position-relative">
          <article
            ref={(el) => (articleContainer = el)}
            class={clsx('col-md-16 col-lg-14 col-xl-12 offset-md-5', styles.articleContent)}
            onClick={handleArticleBodyClick}
          >
            {/*TODO: Check styles.shoutTopic*/}
            <Show when={props.article.layout !== 'audio'}>
              <div class={styles.shoutHeader}>
                <Show when={mainTopic()}>
                  <CardTopic title={mainTopic()?.title || ''} slug={mainTopic()?.slug || ''} />
                </Show>

                <h1>{props.article.title || ''}</h1>
                <Show when={props.article.subtitle}>
                  <h4>{processPrepositions(props.article.subtitle || '')}</h4>
                </Show>

                <div class={styles.shoutAuthor}>
                  <For each={props.article.authors}>
                    {(a: Maybe<Author>, index: () => number) => (
                      <>
                        <Show when={index() > 0}>, </Show>
                        <A href={`/@${a?.slug}`}>{a && getAuthorName(a)}</A>
                      </>
                    )}
                  </For>
                </div>
                <Show
                  when={
                    props.article.cover &&
                    props.article.layout !== 'video' &&
                    props.article.layout !== 'image'
                  }
                >
                  <figure class="img-align-column">
                    <Image
                      width={800}
                      alt={props.article.cover_caption || ''}
                      src={props.article.cover || ''}
                    />
                    <figcaption innerHTML={props.article.cover_caption || ''} />
                  </figure>
                </Show>
              </div>
            </Show>
            <Show when={props.article.lead}>
              <section class={styles.lead} innerHTML={processPrepositions(props.article.lead || '')} />
            </Show>
            <Show when={props.article.layout === 'audio'}>
              <AudioHeader
                title={props.article.title || ''}
                cover={props.article.cover || ''}
                artistData={media()?.[0]}
                topic={mainTopic() as Topic}
              />
              <Show when={media().length > 0}>
                <div class="media-items">
                  <AudioPlayer media={media()} articleSlug={props.article.slug || ''} body={body()} />
                </div>
              </Show>
            </Show>
            <Show when={media() && props.article.layout === 'video'}>
              <div class="media-items">
                <For each={media() || []}>
                  {(m: MediaItem) => (
                    <div class={styles.shoutMediaBody}>
                      <VideoPlayer
                        articleView={true}
                        videoUrl={m.url}
                        title={m.title}
                        description={m.body}
                      />
                      <Show when={m?.body}>
                        <div innerHTML={m.body} />
                      </Show>
                    </div>
                  )}
                </For>
              </div>
            </Show>

            <Show when={body()}>
              <div id="shoutBody" class={styles.shoutBody} innerHTML={body()} />
            </Show>
          </article>

          <Show when={body()}>
            <div class="col-md-6 offset-md-1">
              <TableOfContents variant="article" parentSelector="#shoutBody" body={body()} />
            </div>
          </Show>
        </div>
      </div>

      <Show when={props.article.layout === 'image'}>
        <div class="floor floor--important">
          <div class="wide-container">
            <div class="row">
              <div class="col-md-20 offset-md-2">
                <ImageSwiper images={media()} />
              </div>
            </div>
          </div>
        </div>
      </Show>

      <div class="wide-container">
        <div class="row">
          <div class="col-md-16 offset-md-5">
            <div class={styles.shoutStats}>
              <div class={styles.shoutStatsItem}>
                <ShoutRatingControl
                  shout={props.article}
                  class={styles.ratingControl}
                  ratings={ratings()}
                />
              </div>

              <Popover content={t('Comment')} disabled={isActionPopupActive()}>
                {(triggerRef: (el: HTMLElement) => void) => (
                  <div
                    class={clsx(styles.shoutStatsItem)}
                    ref={triggerRef}
                    onClick={() => commentsRef && scrollTo(commentsRef)}
                  >
                    <Icon name="comment" class={styles.icon} />
                    <Icon name="comment-hover" class={clsx(styles.icon, styles.iconHover)} />
                    <Show
                      when={props.article.stat?.commented}
                      fallback={<span class={styles.commentsTextLabel}>{t('Add comment')}</span>}
                    >
                      {props.article.stat?.commented}
                    </Show>
                  </div>
                )}
              </Popover>

              <Show when={props.article.stat?.viewed}>
                <div class={clsx(styles.shoutStatsItem, styles.shoutStatsItemViews)}>
                  {t('some views', { count: props.article.stat?.viewed || 0 })}
                </div>
              </Show>

              <div class={clsx(styles.shoutStatsItem, styles.shoutStatsItemAdditionalData)}>
                <div class={clsx(styles.shoutStatsItem, styles.shoutStatsItemAdditionalDataItem)}>
                  {formattedDate()}
                </div>
              </div>

              <Popover content={t('Add to bookmarks')} disabled={isActionPopupActive()}>
                {(triggerRef: (el: HTMLElement) => void) => (
                  <div
                    class={clsx(styles.shoutStatsItem, styles.shoutStatsItemBookmarks)}
                    ref={triggerRef}
                    onClick={handleBookmarkButtonClick}
                  >
                    <div class={styles.shoutStatsItemInner}>
                      <Icon name="bookmark" class={styles.icon} />
                      <Icon name="bookmark-hover" class={clsx(styles.icon, styles.iconHover)} />
                    </div>
                  </div>
                )}
              </Popover>

              <Popover content={t('Share')} disabled={isActionPopupActive()}>
                {(triggerRef: (el: HTMLElement) => void) => (
                  <div class={styles.shoutStatsItem} ref={triggerRef}>
                    <SharePopup
                      title={props.article.title}
                      description={props.article.description || body() || media()[0]?.body}
                      imageUrl={props.article.cover || ''}
                      shareUrl={shareUrl()}
                      containerCssClass={stylesHeader.control}
                      onVisibilityChange={(isVisible) => setIsActionPopupActive(isVisible)}
                      trigger={
                        <div class={styles.shoutStatsItemInner}>
                          <Icon name="share-outline" class={styles.icon} />
                          <Icon name="share-outline-hover" class={clsx(styles.icon, styles.iconHover)} />
                        </div>
                      }
                    />
                  </div>
                )}
              </Popover>

              <Show when={canEdit()}>
                <Popover content={t('Edit')}>
                  {(triggerRef: (el: HTMLElement) => void) => (
                    <div class={styles.shoutStatsItem} ref={triggerRef}>
                      <A href={`/edit/${props.article.id}`} class={styles.shoutStatsItemInner}>
                        <Icon name="pencil-outline" class={styles.icon} />
                        <Icon name="pencil-outline-hover" class={clsx(styles.icon, styles.iconHover)} />
                      </A>
                    </div>
                  )}
                </Popover>
              </Show>

              <FeedArticlePopup
                canEdit={Boolean(canEdit())}
                containerCssClass={clsx(stylesHeader.control, styles.articlePopupOpener)}
                onShareClick={() => showModal('share')}
                onInviteClick={() => showModal('inviteMembers')}
                onVisibilityChange={(isVisible) => setIsActionPopupActive(isVisible)}
                trigger={
                  <button>
                    <Icon name="ellipsis" class={clsx(styles.icon)} />
                    <Icon name="ellipsis" class={clsx(styles.icon, styles.iconHover)} />
                  </button>
                }
              />
            </div>

            <Show when={author()?.id && !canEdit()}>
              <div class={styles.help}>
                <button class="button">{t('Cooperate')}</button>
              </div>
            </Show>
            <Show when={canEdit()}>
              <div class={styles.help}>
                <button class="button button--light">{t('Invite to collab')}</button>
              </div>
            </Show>

            <Show when={props.article.topics?.length}>
              <div class={styles.topicsList}>
                <For each={props.article.topics || []}>
                  {(topic) => (
                    <div class={styles.shoutTopic}>
                      <A href={`/topic/${topic?.slug || ''}`}>
                        {lang() === 'en' ? capitalize(topic?.slug || '') : topic?.title || ''}
                      </A>
                    </div>
                  )}
                </For>
              </div>
            </Show>

            <div class={styles.shoutAuthorsList}>
              <Show when={(props.article.authors?.length || 0) > 1}>
                <h4>{t('Authors')}</h4>
              </Show>
              <For each={props.article.authors}>
                {(a: Maybe<Author>) => (
                  <div class="col-xl-12">
                    <AuthorBadge iconButtons={true} showMessageButton={true} author={a as Author} />
                  </div>
                )}
              </For>
            </div>
            <div id="comments" ref={(el) => (commentsRef = el)}>
              <Show when={isReactionsLoaded()}>
                <CommentsTree
                  shoutId={props.article.id}
                  shoutSlug={props.article.slug}
                  articleAuthors={props.article.authors as Author[]}
                />
              </Show>
            </div>
          </div>
        </div>
      </div>
      <Show when={selectedImage()}>
        <Lightbox image={selectedImage()} onClose={handleLightboxClose} />
      </Show>
      <Modal variant="medium" name="inviteMembers">
        <InviteMembers variant={'coauthors'} title={t('Invite experts')} />
      </Modal>
      <ShareModal
        title={props.article.title}
        description={props.article.description || body() || media()[0]?.body}
        imageUrl={props.article.cover || ''}
        shareUrl={shareUrl()}
      />
    </>
  )
}
