import { Button, Scrollbar, Skeleton } from '@cherrystudio/ui'
import Favicon from '@renderer/components/Icons/FallbackFavicon'
import SelectionContextMenu from '@renderer/components/SelectionContextMenu'
import { useTemporaryValue } from '@renderer/hooks/useTemporaryValue'
import type { Citation } from '@renderer/types/message'
import { fetchWebContent, fetchXOEmbed, isXPostUrl, noContent, xOembedKey } from '@renderer/utils/fetch'
import { cleanMarkdownContent } from '@renderer/utils/formats'
import { Check, Copy, FileSearch } from 'lucide-react'
import React, { useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import useSWRImmutable from 'swr/immutable'

import { useOptionalMessageListActions } from '../MessageListProvider'
import type { MessageListActions } from '../types'

type CitationCopyActions = Pick<MessageListActions, 'copyText' | 'notifyError'>
type CitationPanelActions = CitationCopyActions & {
  openPath?: (path: string) => void | Promise<void>
  openExternalUrl?: (url: string) => void | Promise<void>
}

interface CitationsListProps {
  citations: Citation[]
}

interface CitationsPanelContentProps {
  citations: Citation[]
  actions?: CitationPanelActions
}

/**
 * 限制文本长度
 * @param text
 * @param maxLength
 */
const truncateText = (text: string, maxLength = 100) => {
  if (!text) return ''
  return text.length > maxLength ? text.slice(0, maxLength) + '...' : text
}

const getCitationHostname = (citation: Citation) => {
  if (!citation.url) return undefined
  try {
    return new URL(citation.url).hostname
  } catch {
    return undefined
  }
}

const CitationsList: React.FC<CitationsListProps> = ({ citations }) => {
  const { t } = useTranslation()
  const openCitationsPanel = useOptionalMessageListActions()?.openCitationsPanel

  const previewItems = citations.slice(0, 3)
  const count = citations.length
  if (!count) return null

  const handleOpenCitationsPanel = () => {
    openCitationsPanel?.({ citations })
  }

  return (
    <Button
      variant="ghost"
      disabled={!openCitationsPanel}
      onClick={handleOpenCitationsPanel}
      className="mb-2 inline-flex h-8 items-center gap-2 self-start rounded-full border border-border-subtle bg-card px-2.5 py-0 text-xs disabled:opacity-60">
      <div className="flex items-center gap-0.5">
        {previewItems.map((citation) => {
          const hostname = getCitationHostname(citation)
          return (
            <div
              key={`${citation.number}-${citation.url || citation.title}`}
              className="flex size-5 items-center justify-center overflow-hidden rounded-full border border-border-subtle bg-background text-foreground-secondary">
              {citation.type === 'websearch' && hostname ? (
                <Favicon hostname={hostname} alt={citation.title || ''} />
              ) : (
                <FileSearch size={12} strokeWidth={2} />
              )}
            </div>
          )
        })}
      </div>
      <span className="h-3.5 w-px bg-border-subtle" />
      <span className="font-medium text-foreground-secondary">{t('message.citation', { count })}</span>
    </Button>
  )
}

export const CitationsPanelContent: React.FC<CitationsPanelContentProps> = ({ citations, actions }) => {
  return (
    <Scrollbar className="min-h-0 flex-1">
      {citations.map((citation) => (
        <div
          key={citation.url || citation.number || citation.title}
          className="border-border border-b-[0.5px] last:border-b-0">
          {citation.type === 'websearch' && (
            <div className="max-w-[min(400px,60vw)] px-3">
              <WebSearchCitation citation={citation} actions={actions} />
            </div>
          )}
          {citation.type === 'memory' && (
            <div className="max-w-150 px-3">
              <KnowledgeCitation citation={{ ...citation }} actions={actions} />
            </div>
          )}
          {citation.type === 'knowledge' && (
            <div className="max-w-150 px-3">
              <KnowledgeCitation citation={{ ...citation }} actions={actions} />
            </div>
          )}
        </div>
      ))}
    </Scrollbar>
  )
}

const handleLinkClick = (
  url: string,
  event: React.MouseEvent,
  actions?: {
    openPath?: (path: string) => void | Promise<void>
    openExternalUrl?: (url: string) => void | Promise<void>
  }
) => {
  if (!url) return
  if (url.startsWith('http')) {
    if (!actions?.openExternalUrl) return
    event.preventDefault()
    void actions.openExternalUrl(url)
    return
  }

  if (!actions?.openPath) return
  event.preventDefault()
  void actions.openPath(url)
}

const CopyButton: React.FC<{ content: string; actions?: CitationCopyActions }> = ({
  content,
  actions: injectedActions
}) => {
  const [copied, setCopied] = useTemporaryValue(false, 2000)
  const { t } = useTranslation()
  const actions = useOptionalMessageListActions()
  const copyText = injectedActions?.copyText ?? actions?.copyText
  const notifyError = injectedActions?.notifyError ?? actions?.notifyError

  const handleCopy = () => {
    if (!content || !copyText) return
    Promise.resolve(copyText(content, { successMessage: t('common.copied') }))
      .then(() => setCopied(true))
      .catch(() => {
        notifyError?.(t('message.copy.failed'))
      })
  }

  if (!copyText) return null

  return (
    <div
      className="-translate-y-1/2 absolute top-1/2 right-0 flex cursor-pointer items-center justify-center rounded p-1 text-foreground-secondary opacity-0 transition-opacity duration-300 hover:bg-muted hover:opacity-100 group-hover:opacity-100"
      onClick={handleCopy}>
      {copied ? <Check size={14} /> : <Copy size={14} />}
    </div>
  )
}

const WebSearchCitation: React.FC<{ citation: Citation; actions?: CitationPanelActions }> = ({ citation, actions }) => {
  const isXPost = Boolean(citation.url && isXPostUrl(citation.url))
  const providerActions = useOptionalMessageListActions()
  const linkActions = {
    openPath: actions?.openPath ?? providerActions?.openPath,
    openExternalUrl: actions?.openExternalUrl ?? providerActions?.openExternalUrl
  }

  const { data: rawContent, isLoading } = useSWRImmutable(
    citation.url ? `webContent/${citation.url}` : null,
    async () => {
      if (isXPost) {
        const oembed = await fetchXOEmbed(citation.url)
        return oembed ? `@${oembed.author}: ${oembed.text}` : ''
      }
      const res = await fetchWebContent(citation.url, 'markdown')
      // Graceful degrade: fetchWebContent swallows failures into `noContent`, so
      // suppress it to render no snippet (just title + link) rather than placeholder text.
      if (res.content === noContent) return ''
      return cleanMarkdownContent(res.content)
    },
    { shouldRetryOnError: false }
  )
  const fetchedContent = useMemo(() => (rawContent ? truncateText(rawContent, 100) : undefined), [rawContent])

  const { data: oembedData } = useSWRImmutable(
    isXPost && citation.url ? xOembedKey(citation.url) : null,
    () => fetchXOEmbed(citation.url),
    { shouldRetryOnError: false }
  )

  const displayTitle = isXPost && oembedData?.author ? `@${oembedData.author}` : citation.title
  const titleContent = displayTitle || citation.hostname || citation.content || citation.url

  return (
    <SelectionContextMenu>
      <div className="group relative flex w-full flex-col py-3 transition-all duration-300">
        <div className="relative mb-1.5 flex w-full flex-row items-center gap-2">
          {citation.showFavicon && getCitationHostname(citation) && (
            <Favicon hostname={getCitationHostname(citation)!} alt={citation.title || citation.hostname || ''} />
          )}
          {citation.url ? (
            <a
              className="flex-1 text-nowrap text-foreground text-sm leading-[1.6] no-underline"
              href={citation.url}
              onClick={(e) => handleLinkClick(citation.url, e, linkActions)}>
              {displayTitle || <span className="text-primary">{citation.hostname}</span>}
            </a>
          ) : (
            <span className="flex-1 text-nowrap text-foreground text-sm leading-[1.6]">{titleContent}</span>
          )}

          <div className="flex h-3.5 w-3.5 shrink-0 items-center justify-center rounded-full bg-primary/15 text-[10px] text-primary leading-[1.6] opacity-100 transition-opacity duration-300 group-hover:opacity-0">
            {citation.number}
          </div>
          {fetchedContent && <CopyButton content={fetchedContent} actions={actions} />}
        </div>
        {isLoading ? (
          <div className="space-y-1">
            <Skeleton className="h-3 w-full" />
            <Skeleton className="h-3 w-2/3" />
          </div>
        ) : (
          fetchedContent && (
            <div className="selectable-text cursor-text select-text break-all text-[13px] text-foreground-secondary leading-[1.6]">
              {fetchedContent}
            </div>
          )
        )}
      </div>
    </SelectionContextMenu>
  )
}

const KnowledgeCitation: React.FC<{ citation: Citation; actions?: CitationPanelActions }> = ({ citation, actions }) => {
  const providerActions = useOptionalMessageListActions()
  const linkActions = {
    openPath: actions?.openPath ?? providerActions?.openPath,
    openExternalUrl: actions?.openExternalUrl ?? providerActions?.openExternalUrl
  }

  return (
    <SelectionContextMenu>
      <div className="group relative flex w-full flex-col py-3 transition-all duration-300">
        <div className="relative mb-1.5 flex w-full flex-row items-center gap-2">
          {citation.showFavicon && <FileSearch width={16} />}
          <a
            className="flex-1 text-nowrap text-foreground text-sm leading-[1.6] no-underline"
            href={citation.url}
            onClick={(e) => handleLinkClick(citation.url, e, linkActions)}>
            {/* example title: User/path/example.pdf */}
            {citation.title?.split('/').pop()}
          </a>
          <div className="flex h-3.5 w-3.5 shrink-0 items-center justify-center rounded-full bg-primary/15 text-[10px] text-primary leading-[1.6] opacity-100 transition-opacity duration-300 group-hover:opacity-0">
            {citation.number}
          </div>
          {citation.content && <CopyButton content={citation.content} actions={actions} />}
        </div>
        <div className="selectable-text cursor-text select-text break-all text-[13px] text-foreground-secondary leading-[1.6]">
          {citation.content ?? ''}
        </div>
      </div>
    </SelectionContextMenu>
  )
}

export default CitationsList
