<script setup lang="ts">
import { ArrowLeftOutlined, CopyOutlined } from '@ant-design/icons-vue'
import type {
  MonitoringQuestionCitationAnalysisItem,
  MonitoringQuestionDetailCitation,
  MonitoringQuestionDetailPlatform,
  MonitoringSourceItem,
} from '@geo/shared-types'
import { useQuery } from '@tanstack/vue-query'
import dayjs from 'dayjs'
import { computed, nextTick, onBeforeUnmount, ref, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import { useRoute, useRouter } from 'vue-router'

import MarkdownPreview from '@/components/MarkdownPreview.vue'
import { monitoringApi } from '@/lib/api'
import { formatDateTime } from '@/lib/display'
import { formatError } from '@/lib/errors'
import { isMonitoringPlatformId, normalizeMonitoringPlatformId } from '@/lib/monitoring-platforms'
import { useCompanyStore } from '@/stores/company'

const { t } = useI18n()
const route = useRoute()
const router = useRouter()
const companyStore = useCompanyStore()

const trackingMaxHistoryDays = 30
const trackingToday = dayjs().format('YYYY-MM-DD')

const routeBrandId = computed(() => parsePositiveNumber(route.params.brandId))
const brandId = computed(() => companyStore.currentBrandId ?? routeBrandId.value)
const questionId = computed(() => parsePositiveNumber(route.params.questionId))
const questionHash = computed(
  () =>
    normalizeHistoryStateValue(history.state?.tracking_question_hash) ||
    normalizeQueryValue(route.query.question_hash),
)
const questionTitleFallback = computed(
  () =>
    normalizeHistoryStateValue(history.state?.tracking_question_text) ||
    normalizeQueryValue(route.query.question_text),
)
const keywordId = computed(() => normalizeQueryValue(route.query.keyword_id))
const requestedPlatformId = computed(() => {
  const platformId = normalizeMonitoringPlatformId(normalizeQueryValue(route.query.ai_platform_id))
  return isMonitoringPlatformId(platformId) ? platformId : ''
})
const businessDate = computed(
  () => normalizeTrackingBusinessDate(route.query.business_date) || trackingToday,
)
const dateFrom = computed(() => {
  return normalizeQueryValue(route.query.date_from) || businessDate.value
})
const dateTo = computed(() => {
  return normalizeQueryValue(route.query.date_to) || businessDate.value
})

const detailQuery = useQuery({
  queryKey: computed(() => [
    'tracking',
    'question-detail-page',
    brandId.value,
    questionId.value,
    questionHash.value,
    requestedPlatformId.value,
    dateFrom.value,
    dateTo.value,
  ]),
  enabled: computed(() => Boolean(brandId.value && questionId.value)),
  queryFn: () =>
    monitoringApi.questionDetail(brandId.value as number, questionId.value as number, {
      date_from: dateFrom.value,
      date_to: dateTo.value,
      question_hash: questionHash.value || undefined,
      ai_platform_id: requestedPlatformId.value || undefined,
    }),
})

const activePlatformId = ref('')
const detailPlatforms = computed<MonitoringQuestionDetailPlatform[]>(
  () => detailQuery.data.value?.platforms ?? [],
)
const hasNoDetailPlatforms = computed(() => {
  return Boolean(detailQuery.data.value) && detailPlatforms.value.length === 0
})
const shouldShowDetailUnavailableNotice = computed(() => {
  return hasNoDetailPlatforms.value
})
const detailUnavailableNoticeType = computed<'warning' | 'info'>(() => {
  return 'info'
})
const detailUnavailableNoticeTitle = computed(() => {
  return requestedPlatformId.value
    ? t('tracking.selectedPlatformUnauthorizedTitle')
    : t('tracking.noDetailPlatformsTitle')
})
const detailUnavailableNoticeDescription = computed(() => {
  return requestedPlatformId.value
    ? t('tracking.selectedPlatformUnauthorizedDescription')
    : t('tracking.noDetailPlatformsDescription')
})
const detailCitationAnalysis = computed<MonitoringQuestionCitationAnalysisItem[]>(
  () => detailQuery.data.value?.citation_analysis ?? [],
)
const citationSourcesScrollRef = ref<HTMLElement | null>(null)
const highlightedCitationIndex = ref<number | null>(null)

let citationHighlightTimer: ReturnType<typeof setTimeout> | null = null

watch(
  detailPlatforms,
  (platforms) => {
    if (!platforms?.length) {
      activePlatformId.value = ''
      return
    }

    const requestedPlatform = requestedPlatformId.value
      ? platforms.find((item) => item.ai_platform_id === requestedPlatformId.value)
      : null
    const sampledPlatform = platforms.find((item) => item.sample_status === 'sampled')
    const nextPlatform = requestedPlatform ?? sampledPlatform ?? platforms[0]

    if (!platforms.some((item) => item.ai_platform_id === activePlatformId.value)) {
      activePlatformId.value = nextPlatform.ai_platform_id
    }
  },
  { immediate: true },
)

const pageTitle = computed(() => {
  return (
    detailQuery.data.value?.question_text ??
    questionTitleFallback.value ??
    t('tracking.questionDetailFallback')
  )
})

const activePlatform = computed<MonitoringQuestionDetailPlatform | null>(
  () =>
    detailPlatforms.value.find((item) => item.ai_platform_id === activePlatformId.value) ?? null,
)

const activeCitationAnalysis = computed<MonitoringQuestionCitationAnalysisItem[]>(() => {
  const items = detailCitationAnalysis.value
  if (!activePlatformId.value) {
    return items
  }
  return items.filter((item) => item.ai_platform_id === activePlatformId.value)
})

const latestSampledAt = computed<string | null>(() => {
  const platforms = detailPlatforms.value
  const sampledAtValues = platforms
    .map((item) => item.sampled_at)
    .filter((value): value is string => Boolean(value))

  if (!sampledAtValues.length) {
    return null
  }

  return sampledAtValues.reduce((latest, current) => {
    if (!latest) {
      return current
    }
    return dayjs(current).isAfter(dayjs(latest)) ? current : latest
  }, sampledAtValues[0] ?? null)
})

const collectedAtDisplay = computed(() => {
  const sampledAt = activePlatform.value?.sampled_at || latestSampledAt.value
  return sampledAt ? formatDateTime(sampledAt) : '--'
})

type TrackingAnalyzedContentCitation = {
  key: string
  contentName: string
  channelName: string
  citationCount: number
  citationRate: number | null
  citedURL: string | null
}

type TrackingCitationSourceLookupItem = {
  sourceGroupIndex: number
  citation: MonitoringQuestionDetailCitation
}

const renderedAnswerContent = computed<string | null>(() => {
  return formatAnswerContent(activePlatform.value)
})

const activeDeepSeekCitationReferenceLabels = computed<string[][]>(() => {
  return collectDeepSeekCitationReferenceLabels(activePlatform.value)
})

const activeInlineContentCitations = computed<TrackingAnalyzedContentCitation[]>(() => {
  return analyzeInlineContentCitations(activePlatform.value)
})

const shouldShowInlineContentCitations = computed(() => {
  return activeInlineContentCitations.value.length > 0
})

watch(activePlatformId, () => {
  clearCitationHighlight()
})

onBeforeUnmount(() => {
  clearCitationHighlight()
})

function goBack(): void {
  void router.push({
    name: 'tracking',
    query: {
      brand_id: brandId.value ? String(brandId.value) : undefined,
      keyword_id: keywordId.value || undefined,
      ai_platform_id: requestedPlatformId.value || undefined,
      business_date: businessDate.value,
    },
  })
}

function openExternalURL(url: string | null | undefined): void {
  const normalized = String(url ?? '').trim()
  if (!normalized) {
    return
  }
  window.open(normalized, '_blank', 'noopener,noreferrer')
}

function openImitationCreate(
  sourceURL: string | null | undefined,
  sourceTitle?: string | null,
): void {
  const normalizedURL = String(sourceURL ?? '').trim()
  if (!normalizedURL) {
    return
  }

  void router.push({
    name: 'article-imitation-create',
    query: {
      source_url: normalizedURL,
      source_title: String(sourceTitle ?? '').trim() || undefined,
    },
  })
}

function formatPercent(value: number | null | undefined): string {
  if (value === null || value === undefined) {
    return '--'
  }
  return `${(value * 100).toFixed(2)}%`
}

function statusTagColor(status: string): string {
  switch (status) {
    case 'sampled':
      return 'green'
    case 'pending':
      return 'gold'
    case 'not_logged_in':
      return 'default'
    case 'unavailable':
      return 'red'
    default:
      return 'default'
  }
}

function statusLabel(status: string): string {
  return t(`tracking.status.${status}`)
}

function resolveDeepSeekProviderModelLabel(value: string | null | undefined): string {
  const normalized = String(value ?? '').trim()
  if (!normalized || normalized === '--') {
    return 'DeepSeek'
  }

  const compact = normalized.replace(/\s+/g, '').toLowerCase()
  if (/r1|reasoner|deepthink|深度思考/.test(compact)) {
    return 'DeepSeek'
  }

  const versionMatch =
    normalized.match(/deepseek[-_\s]*(r1|v\d+(?:\.\d+)?)/i) ??
    normalized.match(/\b(r1|v\d+(?:\.\d+)?)\b/i)
  if (versionMatch?.[1]) {
    return `DeepSeek-${versionMatch[1].toUpperCase()}`
  }

  if (/search|web|联网|搜索/.test(compact)) {
    return 'DeepSeek'
  }

  return normalized
}

function resolveAnswerProviderModel(platform: MonitoringQuestionDetailPlatform | null): string {
  if (isDeepSeekInlineCitationPlatform(platform?.ai_platform_id)) {
    return resolveDeepSeekProviderModelLabel(platform?.provider_model)
  }

  return String(platform?.provider_model ?? '').trim() || '--'
}

function resolveCitationTitle(citation: MonitoringQuestionDetailCitation): string {
  return citation.cited_title || citation.article_title || citation.cited_url
}

function isLinkedInlineCitationPlatform(platformId: string | null | undefined): boolean {
  return platformId === 'yuanbao'
}

function isKimiInlineCitationPlatform(platformId: string | null | undefined): boolean {
  return platformId === 'kimi'
}

function isDeepSeekInlineCitationPlatform(platformId: string | null | undefined): boolean {
  return platformId === 'deepseek'
}

function supportsContentCitationDisplay(platformId: string | null | undefined): boolean {
  return platformId !== 'qwen'
}

function createQwenSourceGroupPattern(): RegExp {
  return /\[\[source_group_web_(\d+)\]\]/g
}

function createYuanbaoCitationPattern(): RegExp {
  // Match both formats so the same renderer works across the migration:
  //   - "[citation:N]" — legacy yuanbao SSE protocol.
  //   - "[N]" — new hunyuan_t1 protocol; the desktop adapter now lowers
  //     `<div data-idx-list="1,2,10">` placeholders to bare "[N]" and also
  //     normalises any residual "[citation:N]" SSE frames to bare "[N]" so
  //     yuanbao answers render with the same chip-style superscript as
  //     DeepSeek.
  return /\[(?:citation:\s*)?(\d+)\]/gi
}

function createYuanbaoCitationStripPattern(): RegExp {
  // The pattern's `replace` step covers any `[N]` we successfully matched.
  // Strip leftover legacy `[citation:...]` fragments only — we must not
  // strip bare "[N]" tokens here, because formatAnswerContent runs strip
  // *after* replacing matched markers, so any remaining "[N]" is incidental
  // text that should stay untouched.
  return /\[\s*citation\s*:[^\[\]]*?\]/gi
}

function createQwenSourceGroupStripPattern(): RegExp {
  return /\[\[\s*source_group_web_[^\[\]]*?\]\]/gi
}

function createMarkdownLinkPattern(): RegExp {
  return /\[([^\]\n]+)\]\((https?:\/\/[^)\s]+)\)/g
}

function looksLikeHtmlAnswer(value: string | null | undefined): boolean {
  const normalized = String(value ?? '').trim()
  return /<\/?[a-z][^>]*>/i.test(normalized)
}

function parseHtmlFragment(content: string): HTMLElement | null {
  if (!looksLikeHtmlAnswer(content)) {
    return null
  }

  const parser = new DOMParser()
  const document = parser.parseFromString(`<div>${content}</div>`, 'text/html')
  return document.body.firstElementChild instanceof HTMLElement
    ? document.body.firstElementChild
    : null
}

function collectInlineCitationIndexes(answerText: string): number[] {
  const matches = [
    ...answerText.matchAll(createQwenSourceGroupPattern()),
    ...answerText.matchAll(createYuanbaoCitationPattern()),
  ]

  return matches
    .map((match) => Number(match[1]))
    .filter((value) => Number.isFinite(value) && value > 0)
}

function normalizeInlineCitationUrl(value: string | null | undefined): string {
  const normalized = String(value ?? '').trim()
  if (!normalized) {
    return ''
  }

  try {
    const url = new URL(normalized)
    url.hash = ''
    return url.toString()
  } catch {
    return normalized
  }
}

function deriveCitationChannelName(url: string): string {
  try {
    return new URL(url).hostname || '--'
  } catch {
    return '--'
  }
}

function collectKimiInlineCitationOccurrences(answerText: string): Array<{
  url: string
  label: string | null
  citationCount: number
}> {
  const counts = new Map<string, { label: string | null; citationCount: number }>()
  const pattern = createMarkdownLinkPattern()

  for (const match of answerText.matchAll(pattern)) {
    const matchIndex = match.index ?? 0
    if (matchIndex > 0 && answerText[matchIndex - 1] === '!') {
      continue
    }

    const label = String(match[1] ?? '').trim() || null
    const url = normalizeInlineCitationUrl(match[2])
    if (!url) {
      continue
    }

    const existing = counts.get(url)
    if (existing) {
      counts.set(url, {
        label: existing.label ?? label,
        citationCount: existing.citationCount + 1,
      })
      continue
    }

    counts.set(url, {
      label,
      citationCount: 1,
    })
  }

  return Array.from(counts.entries()).map(([url, value]) => ({
    url,
    label: value.label,
    citationCount: value.citationCount,
  }))
}

function buildInlineCitationSourceLookup(
  platform: MonitoringQuestionDetailPlatform | null,
): Map<string, MonitoringSourceItem> {
  const lookup = new Map<string, MonitoringSourceItem>()
  for (const item of platform?.inline_citations ?? []) {
    const normalizedURL = normalizeInlineCitationUrl(item.normalized_url ?? item.url)
    if (!normalizedURL || lookup.has(normalizedURL)) {
      continue
    }
    lookup.set(normalizedURL, item)
  }
  return lookup
}

function buildCitationSourceLookup(
  platform: MonitoringQuestionDetailPlatform | null,
): Map<string, TrackingCitationSourceLookupItem> {
  const lookup = new Map<string, TrackingCitationSourceLookupItem>()
  for (const [index, citation] of (platform?.citations ?? []).entries()) {
    const normalizedURL = normalizeInlineCitationUrl(citation.cited_url)
    if (!normalizedURL || lookup.has(normalizedURL)) {
      continue
    }
    lookup.set(normalizedURL, {
      sourceGroupIndex: index + 1,
      citation,
    })
  }
  return lookup
}

function buildDeepSeekAnswerLinkSourceLookup(
  platform: MonitoringQuestionDetailPlatform | null,
): Map<string, { title: string | null; siteName: string | null }> {
  const lookup = new Map<string, { title: string | null; siteName: string | null }>()

  for (const item of platform?.inline_citations ?? []) {
    const normalizedURL = normalizeInlineCitationUrl(item.normalized_url ?? item.url)
    if (!normalizedURL || lookup.has(normalizedURL)) {
      continue
    }
    lookup.set(normalizedURL, {
      title: item.title?.trim() || null,
      siteName: item.site_name?.trim() || null,
    })
  }

  for (const citation of platform?.citations ?? []) {
    const normalizedURL = normalizeInlineCitationUrl(citation.cited_url)
    if (!normalizedURL || lookup.has(normalizedURL)) {
      continue
    }
    lookup.set(normalizedURL, {
      title: resolveCitationTitle(citation),
      siteName: citation.site_name?.trim() || null,
    })
  }

  return lookup
}

function isHiddenDeepSeekAnswerNode(element: Element): boolean {
  let current: Element | null = element
  for (let depth = 0; current && depth < 8; depth += 1) {
    if (current.hasAttribute('hidden') || current.getAttribute('aria-hidden') === 'true') {
      return true
    }
    const style = current.getAttribute('style') ?? ''
    if (/(display\s*:\s*none|visibility\s*:\s*hidden|opacity\s*:\s*0)/i.test(style)) {
      return true
    }
    current = current.parentElement
  }
  return false
}

function isInsideDeepSeekAuxiliaryLinkContainer(anchor: HTMLAnchorElement): boolean {
  let current = anchor.parentElement
  for (let depth = 0; current && depth < 8; depth += 1) {
    const descriptor = `${current.className || ''} ${current.getAttribute('data-testid') || ''} ${current.getAttribute('role') || ''}`
    if (
      /(search-view-card|search-result|result-card|source-card|reference-card|citation-card|popover|tooltip|dropdown|floating|hover-card|preview-card)/i.test(
        descriptor,
      )
    ) {
      return true
    }
    current = current.parentElement
  }
  return false
}

function shouldCountDeepSeekAnswerLink(anchor: HTMLAnchorElement): boolean {
  if (isHiddenDeepSeekAnswerNode(anchor) || isInsideDeepSeekAuxiliaryLinkContainer(anchor)) {
    return false
  }

  const text = String(anchor.textContent ?? '').trim()
  if (normalizeDeepSeekCitationLabel(text)) {
    return true
  }
  if (shouldStripDeepSeekOrphanAnchor(anchor)) {
    return false
  }
  return /[A-Za-z0-9\u3400-\u9fff]/.test(text)
}

function normalizeDeepSeekCitationLabel(value: string | null | undefined): string | null {
  const normalized = String(value ?? '')
    .trim()
    .replace(/\s+/g, ' ')
  if (!normalized) {
    return null
  }

  const compact = normalized.replace(/\s+/g, '')
  const numericCandidate =
    compact.match(/^\[?(-?\d+)\]?$/)?.[1] ??
    compact.match(/^[（(【](-?\d+)[】)）]$/)?.[1] ??
    compact.match(/^#?(-?\d+)$/)?.[1] ??
    null
  if (numericCandidate) {
    return `[${numericCandidate.replace(/^-/, '')}]`
  }

  return null
}

function getOrCreateDeepSeekFallbackCitationLabel(
  normalizedURL: string,
  fallbackLabels: Map<string, string>,
  state: { nextIndex: number },
): string {
  const existing = fallbackLabels.get(normalizedURL)
  if (existing) {
    return existing
  }

  const nextLabel = `[${state.nextIndex}]`
  fallbackLabels.set(normalizedURL, nextLabel)
  state.nextIndex += 1
  return nextLabel
}

function pushUniqueDeepSeekCitationLabel(labels: string[], label: string): void {
  if (!labels.includes(label)) {
    labels.push(label)
  }
}

function shouldStripDeepSeekOrphanAnchor(anchor: HTMLAnchorElement): boolean {
  const text = String(anchor.textContent ?? '')
    .trim()
    .replace(/\s+/g, '')
  if (/^(link|source|citation|reference|open|原文|链接|来源|引用)$/i.test(text)) {
    return true
  }
  if (/[A-Za-z0-9\u3400-\u9fff]/.test(text)) {
    return false
  }
  if (anchor.querySelector('img, svg')) {
    return true
  }
  return (
    text.length === 0 ||
    /^[\[\](){}<>#.,:;!?+\-_/\\|~`'"“”‘’·•:：，。！？；、🔗↗↘↙↖↪⤴]+$/u.test(text)
  )
}

function collectDeepSeekCitationReferenceLabels(
  platform: MonitoringQuestionDetailPlatform | null,
): string[][] {
  if (!isDeepSeekInlineCitationPlatform(platform?.ai_platform_id)) {
    return []
  }

  const rawAnswer = String(platform?.answer_text ?? '').trim()
  const root = rawAnswer ? parseHtmlFragment(rawAnswer) : null
  if (!root) {
    return (platform?.citations ?? []).map(() => [])
  }

  const citationSourceLookup = buildCitationSourceLookup(platform)
  const labelsByUrl = new Map<string, string[]>()
  const fallbackLabels = new Map<string, string>()
  const fallbackState = { nextIndex: 1 }

  for (const anchor of Array.from(root.querySelectorAll<HTMLAnchorElement>('a[href]'))) {
    const normalizedURL = normalizeInlineCitationUrl(
      anchor.getAttribute('href') || anchor.href || '',
    )
    if (!normalizedURL || !citationSourceLookup.has(normalizedURL)) {
      continue
    }

    const label =
      normalizeDeepSeekCitationLabel(anchor.textContent) ??
      getOrCreateDeepSeekFallbackCitationLabel(normalizedURL, fallbackLabels, fallbackState)
    const labels = labelsByUrl.get(normalizedURL) ?? []
    pushUniqueDeepSeekCitationLabel(labels, label)
    labelsByUrl.set(normalizedURL, labels)
  }

  return (platform?.citations ?? []).map((citation) => {
    const normalizedURL = normalizeInlineCitationUrl(citation.cited_url)
    return normalizedURL ? (labelsByUrl.get(normalizedURL) ?? []) : []
  })
}

function resolveCitationReferenceLabels(
  citationIndex: number,
  platform: MonitoringQuestionDetailPlatform | null,
): string[] {
  if (!isDeepSeekInlineCitationPlatform(platform?.ai_platform_id)) {
    return []
  }

  return activeDeepSeekCitationReferenceLabels.value[citationIndex] ?? []
}

function resolveCitationIndexDisplayLabels(
  citationIndex: number,
  platform: MonitoringQuestionDetailPlatform | null,
): string[] {
  const labels = resolveCitationReferenceLabels(citationIndex, platform)
  if (!labels.length) {
    return []
  }

  const fallbackLabel = `[${citationIndex + 1}]`
  if (labels.length === 1 && labels[0] === fallbackLabel) {
    return []
  }
  return labels
}

function shouldShowCitationSourceListIndex(
  citationIndex: number,
  platform: MonitoringQuestionDetailPlatform | null,
): boolean {
  if (!isDeepSeekInlineCitationPlatform(platform?.ai_platform_id)) {
    return true
  }

  return resolveCitationIndexDisplayLabels(citationIndex, platform).length === 0
}

function countHanCharacters(value: string): number {
  return value.match(/[\u3400-\u9fff]/g)?.length ?? 0
}

function countSuspiciousMojibakeCharacters(value: string): number {
  return value.match(/[\u00c0-\u017f\u2000-\u20ff]/g)?.length ?? 0
}

function looksLikeNonAnswerNoise(value: string): boolean {
  const hanCount = countHanCharacters(value)
  const suspiciousCount = countSuspiciousMojibakeCharacters(value)
  if (hanCount === 0) {
    return suspiciousCount >= 4
  }
  return suspiciousCount >= 8 && suspiciousCount > hanCount * 3
}

function sanitizeAnswerBody(answerText: string | null | undefined): string | null {
  const normalized = String(answerText ?? '').trim()
  if (!normalized) {
    return null
  }

  if (looksLikeHtmlAnswer(normalized)) {
    return normalized
  }

  const lines = normalized
    .split(/\r?\n+/)
    .map((line) => line.trim())
    .filter(Boolean)
  const keptLines = lines.filter((line) => !looksLikeNonAnswerNoise(line))
  const sanitized = (keptLines.length ? keptLines.join('\n') : normalized)
    .replace(/[ \t]+([,.;:!?，。！？；：])/g, '$1')
    .replace(/[ \t]{2,}/g, ' ')
    .replace(/\n{3,}/g, '\n\n')
    .trim()

  return sanitized || null
}

function normalizeLooseMarkdownLine(line: string): string {
  const headingNormalizedLine = line
    .replace(/^[\u200b-\u200f\ufeff]+/, '')
    .replace(/^([ \t]{0,12})(?:\\#|&#35;|&#x23;|＃)/i, '$1#')
  const answerScopedHeading = headingNormalizedLine.replace(
    /^([ \t]{0,12})#{1,2}(?!#)\s*(?=\S)/,
    '$1### ',
  )

  return answerScopedHeading
    .replace(/^([ \t]{0,12})(#{1,6})(?=\S)/, '$1$2 ')
    .replace(/^([ \t]{0,12}#{1,6}\s+)#{1,6}\s*/, '$1')
    .replace(/^([ \t]{0,12}#{1,6}\s+\d+[.)、])(?=\S)/, '$1 ')
    .replace(/^(\s*)([-*+])(?=\S)/, '$1$2 ')
    .replace(/^(\s*)(\d{1,3})([.)])(?=\S)/, '$1$2$3 ')
}

function countMarkdownTablePipes(line: string): number {
  return (line.match(/\|/g) ?? []).length
}

function splitMarkdownTableCells(line: string): string[] {
  return line
    .trim()
    .replace(/^\|/, '')
    .replace(/\|$/, '')
    .split('|')
    .map((cell) => cell.trim())
}

function isMarkdownTableSeparatorLine(line: string): boolean {
  const normalized = line.trim()
  if (!normalized.includes('|')) {
    return false
  }

  const cells = splitMarkdownTableCells(normalized)
  return cells.length >= 2 && cells.every((cell) => /^:?-{3,}:?$/.test(cell))
}

function countMarkdownTableCells(line: string): number {
  return splitMarkdownTableCells(line).length
}

function normalizeMarkdownTableLine(line: string, targetCellCount?: number): string {
  let normalized = line.trim()
  if (!normalized.startsWith('|')) {
    normalized = `| ${normalized}`
  }
  if (!normalized.endsWith('|')) {
    normalized = `${normalized} |`
  }

  if (targetCellCount && targetCellCount > 0) {
    const cells = splitMarkdownTableCells(normalized)
    if (isMarkdownTableSeparatorLine(normalized)) {
      while (cells.length < targetCellCount) {
        cells.push('--------')
      }
    } else {
      while (cells.length < targetCellCount) {
        cells.push('')
      }
    }
    if (cells.length > targetCellCount) {
      cells.length = targetCellCount
    }
    normalized = `| ${cells.join(' | ')} |`
  }

  return normalized.replace(/\s*\|\s*/g, ' | ').replace(/\s{2,}/g, ' ').trim()
}

function normalizeLooseMarkdownTables(content: string): string {
  const detachedTableBlocks = content
    .replace(/\|\|(?=\s*:?-{3,}:?\s*\|)/g, '|\n|')
    .replace(
      /(^|\n)(\s{0,3}#{1,6}\s+[^|\n]{1,80})\|(?=[^|\n]+\|[^\n]*\n\s*\|?\s*:?-{3,}:?\s*\|)/g,
      '$1$2\n|',
    )

  const lines = detachedTableBlocks.split('\n').flatMap((line: string) => {
    const separatorWithFirstRow = line
      .trim()
      .match(/^((?:\|\s*:?-{3,}:?\s*){2,}\|?)(\S.*)$/)
    if (
      !separatorWithFirstRow?.[1] ||
      !separatorWithFirstRow[2] ||
      /^\|*$/.test(separatorWithFirstRow[2].trim())
    ) {
      return [line]
    }
    return [separatorWithFirstRow[1], separatorWithFirstRow[2]]
  })
  const normalizedLines: string[] = []
  let inTable = false
  let tableCellCount = 0

  for (const line of lines) {
    const previousIndex = normalizedLines.length - 1
    if (isMarkdownTableSeparatorLine(line)) {
      tableCellCount =
        previousIndex >= 0
          ? Math.max(
              countMarkdownTableCells(normalizedLines[previousIndex] ?? ''),
              countMarkdownTableCells(line),
            )
          : countMarkdownTableCells(line)
      if (
        previousIndex >= 0 &&
        countMarkdownTablePipes(normalizedLines[previousIndex] ?? '') >= 2
      ) {
        normalizedLines[previousIndex] = normalizeMarkdownTableLine(
          normalizedLines[previousIndex] ?? '',
          tableCellCount,
        )
      }
      normalizedLines.push(normalizeMarkdownTableLine(line, tableCellCount))
      inTable = true
      continue
    }

    if (inTable && countMarkdownTablePipes(line) >= 2) {
      normalizedLines.push(normalizeMarkdownTableLine(line, tableCellCount))
      continue
    }

    inTable = false
    tableCellCount = 0
    normalizedLines.push(line)
  }

  return normalizedLines.join('\n')
}

function normalizeAnswerMarkdown(content: string): string {
  const headingMarkerNormalized = content
    .replace(/(^|\n)[\u200b-\u200f\ufeff]*([ \t]{0,12})(?:\\#|&#35;|&#x23;|＃)/gi, '$1$2#')
    .replace(/([^\n])(?=[\u200b-\u200f\ufeff]*[ \t]{0,12}(?:\\#|&#35;|&#x23;|＃|#){1,6}\s*(?:\d{1,3}[.)、]|[一二三四五六七八九十]+[、.．]|第[一二三四五六七八九十0-9]+))/gi, '$1\n\n')

  const detachedHeadings = normalizeLooseMarkdownTables(headingMarkerNormalized)
    .replace(
      /([^\n])(?=[\u200b-\u200f\ufeff]*[ \t]{0,12}#{1,6}\s*(?:\d{1,3}[.)、]|[一二三四五六七八九十]+[、.．]|第[一二三四五六七八九十0-9]+))/g,
      '$1\n\n',
    )
    .replace(/([^\n])(?=#{2,6}\s*\S)/g, '$1\n\n')
  const lines = detachedHeadings.split('\n')
  let inFence = false

  const normalizedLines = lines.map((line) => {
    if (/^\s*(?:```|~~~)/.test(line)) {
      inFence = !inFence
      return line
    }
    if (inFence) {
      return line
    }
    return normalizeLooseMarkdownLine(line)
  })

  return normalizedLines
    .join('\n')
    .replace(/\n{3,}/g, '\n\n')
    .trim()
}

function buildInlineCitationMarker(
  sourceGroupIndex: number,
  platform: MonitoringQuestionDetailPlatform | null,
): string {
  if (!Number.isFinite(sourceGroupIndex) || sourceGroupIndex < 1) {
    return ''
  }

  const hasCitation = Boolean(platform?.citations?.[sourceGroupIndex - 1])
  const className = hasCitation
    ? 'tracking-inline-citation'
    : 'tracking-inline-citation tracking-inline-citation--muted'

  if (!hasCitation) {
    return `<span class="${className}">[${sourceGroupIndex}]</span>`
  }

  return `<button type="button" class="${className}" data-citation-index="${sourceGroupIndex}">[${sourceGroupIndex}]</button>`
}

function renderDeepSeekAnswerContent(
  answerText: string,
  platform: MonitoringQuestionDetailPlatform | null,
): string {
  const root = parseHtmlFragment(answerText)
  if (!root) {
    return answerText
  }

  const inlineCitationLookup = buildInlineCitationSourceLookup(platform)
  const citationSourceLookup = buildCitationSourceLookup(platform)
  const fallbackLabels = new Map<string, string>()
  const fallbackState = { nextIndex: 1 }

  for (const anchor of Array.from(root.querySelectorAll<HTMLAnchorElement>('a[href], a'))) {
    const rawHref = anchor.hasAttribute('href')
      ? anchor.getAttribute('href') || anchor.href || ''
      : ''
    const normalizedURL = normalizeInlineCitationUrl(rawHref)
    if (!normalizedURL && !shouldStripDeepSeekOrphanAnchor(anchor)) {
      continue
    }

    const source = inlineCitationLookup.get(normalizedURL) ?? null
    const citationSource = citationSourceLookup.get(normalizedURL) ?? null
    if (!source && !citationSource) {
      if (shouldStripDeepSeekOrphanAnchor(anchor)) {
        anchor.remove()
      } else {
        anchor.replaceWith(document.createTextNode(anchor.textContent ?? ''))
      }
      continue
    }

    anchor.setAttribute('href', normalizedURL)
    anchor.setAttribute('target', '_blank')
    anchor.setAttribute('rel', 'noreferrer')
    anchor.setAttribute('class', 'tracking-inline-citation tracking-inline-citation--content')
    if (citationSource) {
      anchor.setAttribute('data-citation-index', String(citationSource.sourceGroupIndex))
    } else {
      anchor.removeAttribute('data-citation-index')
    }

    const title = citationSource
      ? resolveCitationTitle(citationSource.citation)
      : source?.title?.trim() || ''
    if (title) {
      anchor.setAttribute('title', title)
    } else {
      anchor.removeAttribute('title')
    }

    const displayLabel =
      normalizeDeepSeekCitationLabel(anchor.textContent) ??
      getOrCreateDeepSeekFallbackCitationLabel(normalizedURL, fallbackLabels, fallbackState)
    anchor.textContent = displayLabel
  }

  return root.innerHTML.trim() || answerText
}

function formatAnswerContent(platform: MonitoringQuestionDetailPlatform | null): string | null {
  const rawAnswer = String(platform?.answer_text ?? '').trim()
  if (!rawAnswer) {
    return null
  }

  if (
    isDeepSeekInlineCitationPlatform(platform?.ai_platform_id) &&
    looksLikeHtmlAnswer(rawAnswer)
  ) {
    return renderDeepSeekAnswerContent(rawAnswer, platform)
  }

  if (looksLikeHtmlAnswer(rawAnswer)) {
    return rawAnswer
  }

  const sanitized = sanitizeAnswerBody(rawAnswer)
  if (!sanitized) {
    return null
  }

  const withInlineMarkers = isLinkedInlineCitationPlatform(platform?.ai_platform_id)
    ? sanitized
        .replace(createQwenSourceGroupPattern(), (_, sourceGroupIndex) => {
          return buildInlineCitationMarker(Number(sourceGroupIndex), platform)
        })
        .replace(createYuanbaoCitationPattern(), (_, sourceGroupIndex) => {
          return buildInlineCitationMarker(Number(sourceGroupIndex), platform)
        })
    : sanitized

  const normalizedMarkdown = withInlineMarkers
    .replace(createQwenSourceGroupStripPattern(), '')
    .replace(createYuanbaoCitationStripPattern(), '')
    .replace(/[ \t]{2,}/g, ' ')
    .replace(/\n{3,}/g, '\n\n')
    .trim()

  return normalizeAnswerMarkdown(normalizedMarkdown)
}

function collectDeepSeekInlineCitationOccurrences(
  answerText: string,
  platform: MonitoringQuestionDetailPlatform | null,
): Array<{
  url: string
  label: string | null
  siteName: string | null
  citationCount: number
}> {
  const root = parseHtmlFragment(answerText)
  if (!root) {
    return []
  }

  const sourceLookup = buildDeepSeekAnswerLinkSourceLookup(platform)

  const counts = new Map<
    string,
    { label: string | null; siteName: string | null; citationCount: number }
  >()
  for (const anchor of Array.from(root.querySelectorAll<HTMLAnchorElement>('a[href]'))) {
    const normalizedURL = normalizeInlineCitationUrl(
      anchor.getAttribute('href') || anchor.href || '',
    )
    if (!normalizedURL || !shouldCountDeepSeekAnswerLink(anchor)) {
      continue
    }

    const source = sourceLookup.get(normalizedURL) ?? null
    const existing = counts.get(normalizedURL)
    if (existing) {
      // One row per URL, but every visible answer occurrence contributes to the count.
      existing.citationCount += 1
      continue
    }

    counts.set(normalizedURL, {
      label: source?.title || anchor.getAttribute('title')?.trim() || null,
      siteName: source?.siteName || null,
      citationCount: 1,
    })
  }

  return Array.from(counts.entries()).map(([url, value]) => ({
    url,
    label: value.label,
    siteName: value.siteName,
    citationCount: value.citationCount,
  }))
}

function analyzeInlineContentCitations(
  platform: MonitoringQuestionDetailPlatform | null,
): TrackingAnalyzedContentCitation[] {
  const answerText = String(platform?.answer_text ?? '')
  if (!answerText) {
    return []
  }

  if (isDeepSeekInlineCitationPlatform(platform?.ai_platform_id)) {
    const occurrences = collectDeepSeekInlineCitationOccurrences(answerText, platform)
    if (!occurrences.length) {
      return []
    }

    const totalCitationCount = occurrences.reduce((sum, item) => sum + item.citationCount, 0)
    if (!totalCitationCount) {
      return []
    }

    return occurrences
      .sort((left, right) => {
        if (right.citationCount !== left.citationCount) {
          return right.citationCount - left.citationCount
        }
        return left.url.localeCompare(right.url, 'zh-CN')
      })
      .map((item) => ({
        key: `deepseek-${item.url}`,
        contentName: item.label || item.url,
        channelName: item.siteName || deriveCitationChannelName(item.url),
        citationCount: item.citationCount,
        citationRate: item.citationCount / totalCitationCount,
        citedURL: item.url,
      }))
  }

  if (isKimiInlineCitationPlatform(platform?.ai_platform_id)) {
    const occurrences = collectKimiInlineCitationOccurrences(answerText)
    if (!occurrences.length) {
      return []
    }

    const totalCitationCount = occurrences.reduce((sum, item) => sum + item.citationCount, 0)
    if (!totalCitationCount) {
      return []
    }

    const inlineCitationLookup = buildInlineCitationSourceLookup(platform)
    return occurrences
      .sort((left, right) => {
        if (right.citationCount !== left.citationCount) {
          return right.citationCount - left.citationCount
        }
        return left.url.localeCompare(right.url, 'zh-CN')
      })
      .map((item) => {
        const source = inlineCitationLookup.get(item.url)
        const contentName = source?.title?.trim() || item.label || item.url
        const channelName = source?.site_name?.trim() || deriveCitationChannelName(item.url)
        return {
          key: `kimi-${item.url}`,
          contentName,
          channelName,
          citationCount: item.citationCount,
          citationRate: item.citationCount / totalCitationCount,
          citedURL: item.url,
        }
      })
  }

  if (!isLinkedInlineCitationPlatform(platform?.ai_platform_id)) {
    return []
  }

  const sourceGroupIndexes = collectInlineCitationIndexes(answerText)
  if (!sourceGroupIndexes.length) {
    return []
  }

  const citationCounts = new Map<number, number>()
  for (const sourceGroupIndex of sourceGroupIndexes) {
    citationCounts.set(sourceGroupIndex, (citationCounts.get(sourceGroupIndex) ?? 0) + 1)
  }

  const totalCitationCount = Array.from(citationCounts.values()).reduce(
    (sum, count) => sum + count,
    0,
  )
  if (!totalCitationCount) {
    return []
  }

  return Array.from(citationCounts.entries())
    .sort((left, right) => {
      if (right[1] !== left[1]) {
        return right[1] - left[1]
      }
      return left[0] - right[0]
    })
    .flatMap(([sourceGroupIndex, citationCount]) => {
      const citation = platform?.citations?.[sourceGroupIndex - 1] ?? null
      const citedURL = citation?.cited_url?.trim() ?? ''
      if (!citedURL) {
        return []
      }

      return [
        {
          key: `${sourceGroupIndex}-${citedURL}`,
          contentName: citation ? resolveCitationTitle(citation) : `引用内容 #${sourceGroupIndex}`,
          channelName: citation?.site_name ?? '--',
          citationCount,
          citationRate: citationCount / totalCitationCount,
          citedURL,
        },
      ]
    })
}

function clearCitationHighlight(): void {
  if (citationHighlightTimer !== null) {
    clearTimeout(citationHighlightTimer)
    citationHighlightTimer = null
  }
  highlightedCitationIndex.value = null
}

async function focusCitationSource(sourceGroupIndex: number): Promise<void> {
  if (!Number.isFinite(sourceGroupIndex) || sourceGroupIndex < 1) {
    return
  }

  await nextTick()
  const citationCard = citationSourcesScrollRef.value?.querySelector<HTMLElement>(
    `[data-citation-index="${sourceGroupIndex}"]`,
  )
  if (!citationCard) {
    return
  }

  highlightedCitationIndex.value = sourceGroupIndex
  citationCard.scrollIntoView({
    behavior: 'smooth',
    block: 'nearest',
    inline: 'nearest',
  })

  if (citationHighlightTimer !== null) {
    clearTimeout(citationHighlightTimer)
  }
  citationHighlightTimer = setTimeout(() => {
    highlightedCitationIndex.value = null
    citationHighlightTimer = null
  }, 2400)
}

function handleAnswerContentClick(event: MouseEvent): void {
  const target = event.target
  if (!(target instanceof HTMLElement)) {
    return
  }

  const trigger = target.closest('[data-citation-index]') as HTMLElement | null
  if (!trigger) {
    return
  }

  const sourceGroupIndex = Number(trigger.dataset.citationIndex ?? '')
  if (!Number.isFinite(sourceGroupIndex) || sourceGroupIndex < 1) {
    return
  }

  event.preventDefault()
  void focusCitationSource(sourceGroupIndex)
}

function normalizeQueryValue(value: unknown): string {
  const raw = Array.isArray(value) ? value[0] : value
  return String(raw ?? '').trim()
}

function normalizeHistoryStateValue(value: unknown): string {
  return typeof value === 'string' ? value.trim() : ''
}

function normalizeTrackingBusinessDate(value: unknown): string | null {
  const normalized = normalizeQueryValue(value)
  if (!normalized) {
    return null
  }
  const parsed = dayjs(normalized)
  if (!parsed.isValid() || parsed.format('YYYY-MM-DD') !== normalized) {
    return null
  }

  const today = dayjs().startOf('day')
  const earliestAllowed = today.subtract(trackingMaxHistoryDays - 1, 'day')
  if (parsed.isAfter(today, 'day') || parsed.isBefore(earliestAllowed, 'day')) {
    return null
  }
  return normalized
}

function parsePositiveNumber(value: unknown): number | null {
  const raw = Array.isArray(value) ? value[0] : value
  const parsed = Number(raw)
  if (!Number.isFinite(parsed) || parsed <= 0) {
    return null
  }
  return parsed
}

const citationAnalysisColumns = computed(() => [
  {
    title: t('tracking.columns.sourcePlatform'),
    key: 'sourcePlatform',
    dataIndex: 'site_name',
    width: '30%',
  },
  {
    title: t('tracking.columns.domain'),
    key: 'domain',
    dataIndex: 'site_domain',
    width: '30%',
    align: 'center' as const,
  },
  {
    title: t('tracking.columns.citations'),
    key: 'citationCount',
    dataIndex: 'citation_count',
    align: 'center' as const,
    width: '20%',
  },
  {
    title: t('tracking.columns.share'),
    key: 'citationRate',
    dataIndex: 'citation_rate',
    align: 'center' as const,
    width: '20%',
  },
])

const contentCitationColumns = computed(() => [
  {
    title: t('tracking.columns.contentName'),
    key: 'contentName',
    dataIndex: 'contentName',
    width: '38%',
  },
  {
    title: t('tracking.columns.channelName'),
    key: 'channelName',
    dataIndex: 'channelName',
    width: '20%',
    align: 'center' as const,
  },
  {
    title: t('tracking.columns.citations'),
    key: 'citationCount',
    dataIndex: 'citationCount',
    align: 'center' as const,
    width: '16%',
  },
  {
    title: t('tracking.columns.share'),
    key: 'citationRate',
    dataIndex: 'citationRate',
    align: 'center' as const,
    width: '16%',
  },
  { title: t('common.actions'), key: 'actions', align: 'center' as const, width: '10%' },
])
</script>

<template>
  <section class="tracking-question-page">
    <div class="tracking-question-page__hero">
      <div class="tracking-question-page__header-top">
        <button type="button" class="tracking-question-page__back" @click="goBack">
          <ArrowLeftOutlined />
          <span>{{ t('tracking.backToQuestions') }}</span>
        </button>
      </div>

      <div class="tracking-question-page__copy">
        <p class="tracking-question-page__eyebrow">{{ t('tracking.questionDetailEyebrow') }}</p>
        <h1>{{ pageTitle }}</h1>
        <p class="tracking-question-page__meta">
          {{ t('tracking.questionDetailCollectedAt', { time: collectedAtDisplay }) }}
        </p>
      </div>
    </div>

    <a-alert
      v-if="detailQuery.error.value"
      type="error"
      show-icon
      :message="formatError(detailQuery.error.value)"
      class="tracking-question-page__alert"
    />

    <a-spin :spinning="detailQuery.isLoading.value">
      <div class="tracking-question-page__content">
        <a-alert
          v-if="shouldShowDetailUnavailableNotice"
          :type="detailUnavailableNoticeType"
          show-icon
          :message="detailUnavailableNoticeTitle"
          :description="detailUnavailableNoticeDescription"
          class="tracking-question-page__alert"
        />

        <div v-if="!shouldShowDetailUnavailableNotice" class="tracking-question-page__tabs-wrapper">
          <a-tabs v-model:activeKey="activePlatformId" size="large" class="tracking-question-tabs">
            <a-tab-pane v-for="platform in detailPlatforms" :key="platform.ai_platform_id">
              <template #tab>
                <span :class="platform.sample_status !== 'sampled' ? 'is-muted' : ''">
                  {{ platform.platform_name }}
                </span>
              </template>
            </a-tab-pane>
          </a-tabs>
        </div>

        <div v-if="!shouldShowDetailUnavailableNotice" class="tracking-question-grid">
          <section class="tracking-question-card tracking-question-card--answer">
            <div class="tracking-question-card__header">
              <div class="tracking-question-card__section-title">
                <span class="tracking-question-card__accent" />
                <span>{{ t('tracking.answerContent') }}</span>
              </div>
            </div>

            <div class="tracking-question-card__body">
              <div class="tracking-question-answer__headline">
                <h2>{{ activePlatform?.platform_name ?? t('common.noData') }}</h2>
                <a-tag :color="statusTagColor(activePlatform?.sample_status ?? 'pending')">
                  {{ statusLabel(activePlatform?.sample_status ?? 'pending') }}
                </a-tag>
              </div>

              <div class="tracking-question-answer__content custom-scrollbar">
                <MarkdownPreview
                  v-if="renderedAnswerContent"
                  :content="renderedAnswerContent"
                  @click="handleAnswerContentClick"
                />
                <a-empty v-else :description="t('tracking.noAnswer')" />
              </div>

              <div class="tracking-question-answer__meta">
                <span>
                  {{
                    activePlatform?.sampled_at ? formatDateTime(activePlatform.sampled_at) : '--'
                  }}
                </span>
                <span>{{ resolveAnswerProviderModel(activePlatform) }}</span>
              </div>
            </div>
          </section>

          <section class="tracking-question-card">
            <div class="tracking-question-card__header">
              <div class="tracking-question-card__section-title">
                <span class="tracking-question-card__accent" />
                <span>{{ t('tracking.citationSourcesTitle') }}</span>
              </div>
            </div>

            <div
              ref="citationSourcesScrollRef"
              class="tracking-question-card__scroll-area custom-scrollbar"
            >
              <div v-if="activePlatform?.citations?.length" class="tracking-question-source-list">
                <div
                  v-for="(citation, index) in activePlatform.citations"
                  :key="citation.cited_url"
                  role="link"
                  tabindex="0"
                  class="tracking-question-source-card"
                  :class="{ 'is-linked': highlightedCitationIndex === index + 1 }"
                  :data-citation-index="index + 1"
                  @click="openExternalURL(citation.cited_url)"
                  @keydown.enter="openExternalURL(citation.cited_url)"
                >
                  <div class="tracking-question-source-card__headline">
                    <div class="tracking-question-source-card__indexes">
                      <span
                        v-for="label in resolveCitationIndexDisplayLabels(index, activePlatform)"
                        :key="`${citation.cited_url}-${label}`"
                        class="tracking-question-source-card__reference-badge"
                      >
                        {{ label }}
                      </span>
                      <span
                        v-if="shouldShowCitationSourceListIndex(index, activePlatform)"
                        class="tracking-question-source-card__index"
                        :class="{
                          'tracking-question-source-card__index--secondary':
                            resolveCitationIndexDisplayLabels(index, activePlatform).length > 0,
                        }"
                      >
                        [{{ index + 1 }}]
                      </span>
                    </div>
                    <strong>{{ citation.site_name }}</strong>
                    <span
                      v-if="resolveCitationTitle(citation)"
                      class="tracking-question-source-card__divider"
                    >
                      ·
                    </span>
                    <span
                      class="tracking-question-source-card__title"
                      :title="resolveCitationTitle(citation)"
                    >
                      {{ resolveCitationTitle(citation) }}
                    </span>
                    <a-tooltip :title="t('tracking.imitationAction')">
                      <button
                        type="button"
                        class="tracking-question-imitation-trigger tracking-question-imitation-trigger--source"
                        @click.stop="
                          openImitationCreate(citation.cited_url, resolveCitationTitle(citation))
                        "
                      >
                        <CopyOutlined />
                      </button>
                    </a-tooltip>
                    <a-tag
                      v-if="
                        citation.article_id &&
                        supportsContentCitationDisplay(activePlatform?.ai_platform_id)
                      "
                      color="blue"
                      class="tracking-question-source-card__badge"
                    >
                      {{ t('tracking.contentCitationBadge') }}
                    </a-tag>
                  </div>
                  <div class="tracking-question-source-card__link">
                    {{ citation.cited_url }}
                  </div>
                </div>
              </div>
              <a-empty v-else :description="t('tracking.noCitations')" />
            </div>
          </section>

          <!-- Citation Analysis -->
          <section
            :class="[
              'tracking-question-card',
              !shouldShowInlineContentCitations ? 'tracking-question-card--span-2' : '',
            ]"
          >
            <div class="tracking-question-card__header">
              <div class="tracking-question-card__section-title">
                <span class="tracking-question-card__accent" />
                <span>{{ t('tracking.citationAnalysisTitle') }}</span>
              </div>
            </div>

            <div
              class="tracking-question-card__scroll-area custom-scrollbar tracking-question-table-wrap"
            >
              <a-table
                v-if="activeCitationAnalysis.length"
                row-key="site_key"
                class="modern-table"
                :columns="citationAnalysisColumns"
                :data-source="activeCitationAnalysis"
                :pagination="false"
                size="middle"
              >
                <template #bodyCell="{ column, record }">
                  <template v-if="column.key === 'sourcePlatform'">
                    <div class="cell-primary-stack">
                      <strong class="table-text" :title="record.site_name">
                        {{ record.site_name }}
                      </strong>
                      <small
                        v-if="
                          record.content_citation_count > 0 &&
                          supportsContentCitationDisplay(record.ai_platform_id)
                        "
                        class="meta-subtext"
                      >
                        {{
                          t('tracking.contentCitationMeta', {
                            count: record.content_citation_count,
                          })
                        }}
                      </small>
                    </div>
                  </template>
                  <template v-else-if="column.key === 'domain'">
                    <span class="tracking-question-table__secondary">
                      {{ record.site_domain || record.site_key }}
                    </span>
                  </template>
                  <template v-else-if="column.key === 'citationCount'">
                    <span class="metric-value">{{ record.citation_count }}</span>
                  </template>
                  <template v-else-if="column.key === 'citationRate'">
                    <strong class="metric-highlight">
                      {{ formatPercent(record.citation_rate) }}
                    </strong>
                  </template>
                </template>
              </a-table>
              <a-empty
                v-else
                :description="t('tracking.noCitationAnalysis')"
                class="tracking-question-empty"
              />
            </div>
          </section>

          <!-- Content Citations -->
          <section v-if="shouldShowInlineContentCitations" class="tracking-question-card">
            <div class="tracking-question-card__header">
              <div class="tracking-question-card__section-title">
                <span class="tracking-question-card__accent" />
                <span>{{ t('tracking.contentCitationsTitle') }}</span>
              </div>
            </div>

            <div
              class="tracking-question-card__scroll-area custom-scrollbar tracking-question-table-wrap"
            >
              <a-table
                row-key="key"
                class="modern-table"
                :columns="contentCitationColumns"
                :data-source="activeInlineContentCitations"
                :pagination="false"
                size="middle"
              >
                <template #bodyCell="{ column, record }">
                  <template v-if="column.key === 'contentName'">
                    <div class="cell-primary-stack">
                      <a
                        v-if="record.citedURL"
                        :href="record.citedURL"
                        target="_blank"
                        rel="noreferrer"
                        :title="record.contentName"
                        class="table-link"
                      >
                        {{ record.contentName }}
                      </a>
                      <strong v-else class="table-text" :title="record.contentName">
                        {{ record.contentName }}
                      </strong>
                    </div>
                  </template>
                  <template v-else-if="column.key === 'channelName'">
                    <span class="tracking-question-table__secondary" :title="record.channelName">
                      {{ record.channelName }}
                    </span>
                  </template>
                  <template v-else-if="column.key === 'citationCount'">
                    <span class="metric-value">{{ record.citationCount }}</span>
                  </template>
                  <template v-else-if="column.key === 'citationRate'">
                    <strong class="metric-highlight">
                      {{ formatPercent(record.citationRate) }}
                    </strong>
                  </template>
                  <template v-else-if="column.key === 'actions'">
                    <a-tooltip v-if="record.citedURL" :title="t('tracking.imitationAction')">
                      <button
                        type="button"
                        class="tracking-question-imitation-trigger tracking-question-imitation-trigger--table-action"
                        @click.stop="openImitationCreate(record.citedURL, record.contentName)"
                      >
                        <CopyOutlined />
                      </button>
                    </a-tooltip>
                    <span v-else class="tracking-question-table__empty-action">--</span>
                  </template>
                </template>
              </a-table>
            </div>
          </section>
        </div>
      </div>
    </a-spin>
  </section>
</template>

<style scoped>
.tracking-question-page {
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.tracking-question-page__content {
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.tracking-question-page__hero {
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding: 24px 32px;
  background: #ffffff;
  border: 1px solid #e8edf5;
  border-radius: 20px;
  box-shadow: 0 4px 12px rgba(15, 23, 42, 0.02);
}

.tracking-question-page__header-top {
  display: flex;
}

.tracking-question-page__back {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  height: 32px;
  padding: 0 12px 0 8px;
  border: none;
  border-radius: 6px;
  background: transparent;
  color: #64748b;
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.2s ease;
  margin-left: -8px;
}

.tracking-question-page__back:hover {
  background: #e2e8f0;
  color: #0f172a;
}

.tracking-question-page__copy {
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-width: 0;
}

.tracking-question-page__eyebrow {
  margin: 0;
  color: #2563eb;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.tracking-question-page__copy h1 {
  margin: 0;
  color: #0f172a;
  font-size: 32px;
  font-weight: 800;
  line-height: 1.25;
  letter-spacing: -0.02em;
}

.tracking-question-page__meta {
  margin: 8px 0 0;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 6px 12px;
  background: #f1f5f9;
  border-radius: 6px;
  color: #475569;
  font-size: 14px;
  font-weight: 500;
  width: fit-content;
}

.tracking-question-page__alert {
  border-radius: 16px;
}

.tracking-question-page__tabs-wrapper {
  background: #ffffff;
  padding: 4px 24px 0;
  border-radius: 16px;
  border: 1px solid #e8edf5;
  box-shadow: 0 4px 12px rgba(15, 23, 42, 0.02);
}

.tracking-question-tabs :deep(.ant-tabs-nav) {
  margin-bottom: 0;
}

.tracking-question-tabs .is-muted {
  opacity: 0.65;
}

.tracking-question-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.45fr) minmax(0, 1fr);
  grid-auto-rows: 800px;
  gap: 20px;
}

.tracking-question-card {
  min-width: 0;
  display: flex;
  flex-direction: column;
  padding: 24px 28px;
  border: 1px solid #e8edf5;
  border-radius: 20px;
  background: #ffffff;
  box-shadow: 0 10px 30px rgba(15, 23, 42, 0.03);
}

.tracking-question-card--answer {
  /* removed min-height, grid-auto-rows handles this universally */
}

.tracking-question-card--span-2 {
  grid-column: 1 / -1;
}

.tracking-question-card__header {
  margin-bottom: 20px;
  flex-shrink: 0;
}

.tracking-question-card__header--split {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
}

.tracking-question-card__section-title {
  display: inline-flex;
  align-items: center;
  gap: 14px;
  color: #111827;
  font-size: 20px;
  font-weight: 700;
}

.tracking-question-card__accent {
  display: inline-block;
  width: 6px;
  height: 28px;
  border-radius: 999px;
  background: #2563eb;
}

.tracking-question-card__hint {
  margin: 0;
  max-width: 420px;
  color: #94a3b8;
  font-size: 13px;
  line-height: 1.6;
  text-align: right;
}

.tracking-question-card__body {
  display: flex;
  flex: 1;
  min-height: 0;
  flex-direction: column;
}

.tracking-question-card__scroll-area {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  margin-right: -12px;
  padding-right: 12px;
}

.tracking-question-table-wrap {
  margin: 0;
  padding: 0;
}

.tracking-question-empty {
  margin-top: 40px;
}

.tracking-question-answer__headline {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 18px;
  flex-shrink: 0;
}

.tracking-question-answer__content {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  margin-right: -12px;
  padding-right: 12px;
  margin-bottom: 18px;
}

.tracking-question-answer__content :deep(.markdown-preview) {
  color: #111827;
  font-size: 15px;
  line-height: 1.85;
  overflow-wrap: anywhere;
}

.tracking-question-answer__content :deep(.markdown-preview h1),
.tracking-question-answer__content :deep(.markdown-preview h2),
.tracking-question-answer__content :deep(.markdown-preview h3),
.tracking-question-answer__content :deep(.markdown-preview h4),
.tracking-question-answer__content :deep(.markdown-preview h5),
.tracking-question-answer__content :deep(.markdown-preview h6) {
  margin: 20px 0 10px;
  color: #0f172a;
  line-height: 1.35;
  letter-spacing: 0;
}

.tracking-question-answer__content :deep(.markdown-preview h1) {
  font-size: 24px;
}

.tracking-question-answer__content :deep(.markdown-preview h2) {
  font-size: 21px;
}

.tracking-question-answer__content :deep(.markdown-preview h3) {
  font-size: 18px;
}

.tracking-question-answer__content :deep(.markdown-preview h4),
.tracking-question-answer__content :deep(.markdown-preview h5),
.tracking-question-answer__content :deep(.markdown-preview h6) {
  font-size: 16px;
}

.tracking-question-answer__content :deep(.markdown-preview p),
.tracking-question-answer__content :deep(.markdown-preview ul),
.tracking-question-answer__content :deep(.markdown-preview ol),
.tracking-question-answer__content :deep(.markdown-preview blockquote),
.tracking-question-answer__content :deep(.markdown-preview table) {
  margin: 0 0 14px;
}

.tracking-question-answer__content :deep(.markdown-preview ul),
.tracking-question-answer__content :deep(.markdown-preview ol) {
  padding-left: 22px;
}

.tracking-question-answer__content :deep(.markdown-preview li + li) {
  margin-top: 6px;
}

.tracking-question-answer__content :deep(.markdown-preview strong) {
  color: #0f172a;
  font-weight: 700;
}

.tracking-question-answer__content :deep(.markdown-preview hr) {
  margin: 22px 0;
}

.tracking-question-answer__content :deep(.markdown-preview table) {
  display: block;
  max-width: 100%;
  overflow-x: auto;
  border-radius: 8px;
}

.custom-scrollbar::-webkit-scrollbar {
  width: 6px;
}

.custom-scrollbar::-webkit-scrollbar-track {
  background: transparent;
}

.custom-scrollbar::-webkit-scrollbar-thumb {
  background: #e2e8f0;
  border-radius: 4px;
}

.custom-scrollbar::-webkit-scrollbar-thumb:hover {
  background: #cbd5e1;
}

.tracking-question-answer__headline h2 {
  margin: 0;
  font-size: 22px;
}

.tracking-question-answer__meta {
  display: flex;
  flex-wrap: wrap;
  gap: 18px;
  margin-top: auto;
  padding-top: 18px;
  color: #9ca3af;
  font-size: 13px;
}

.tracking-question-source-list,
.tracking-question-content-list {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.tracking-question-source-card,
.tracking-question-content-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 12px 16px;
  border: 1px solid #edf2f7;
  border-radius: 10px;
  background: #f8fbff;
  cursor: pointer;
  transition: all 0.2s ease;
  text-decoration: none;
}

.tracking-question-source-card:hover,
.tracking-question-content-card:hover {
  border-color: #dbeafe;
  box-shadow: 0 4px 12px rgba(37, 99, 235, 0.05);
}

.tracking-question-source-card {
  padding-right: 58px;
}

.tracking-question-source-card.is-linked {
  border-color: #93c5fd;
  background: #eef6ff;
  box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.12);
}

.tracking-question-source-card__headline {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
}

.tracking-question-source-card__indexes {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
}

.tracking-question-source-card__index {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 34px;
  height: 24px;
  padding: 0 8px;
  border-radius: 999px;
  background: #e8f1ff;
  color: #1d4ed8;
  font-size: 12px;
  font-weight: 700;
  flex-shrink: 0;
}

.tracking-question-source-card__index--secondary {
  background: #f8fafc;
  color: #64748b;
  border: 1px solid #e2e8f0;
}

.tracking-question-source-card__reference-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 34px;
  height: 24px;
  padding: 0 8px;
  border-radius: 999px;
  border: 1px solid #bfdbfe;
  background: #eff6ff;
  color: #2563eb;
  font-size: 12px;
  font-weight: 700;
  line-height: 1;
  flex-shrink: 0;
}

.tracking-question-source-card__headline strong {
  display: block;
  color: #2563eb;
  font-size: 14px;
  font-weight: 600;
  white-space: nowrap;
}

.tracking-question-source-card__divider {
  color: #cbd5e1;
  font-weight: bold;
}

.tracking-question-source-card__title {
  color: #475569;
  font-size: 14px;
  flex: 1;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.tracking-question-source-card__badge {
  margin: 0;
  margin-left: auto;
  white-space: nowrap;
}

.tracking-question-imitation-trigger {
  display: inline-flex;
  width: 28px;
  height: 28px;
  flex-shrink: 0;
  align-items: center;
  justify-content: center;
  border: 1px solid #dbeafe;
  border-radius: 8px;
  background: #ffffff;
  color: #2563eb;
  cursor: pointer;
  opacity: 0;
  transform: translateY(1px);
  transition:
    opacity 0.18s ease,
    transform 0.18s ease,
    border-color 0.18s ease,
    background-color 0.18s ease;
}

.tracking-question-imitation-trigger:hover {
  border-color: #93c5fd;
  background: #eff6ff;
}

.tracking-question-imitation-trigger--source {
  position: absolute;
  top: 50%;
  right: 16px;
  transform: translateY(calc(-50% + 1px));
}

.tracking-question-source-card:hover .tracking-question-imitation-trigger,
.tracking-question-source-card:focus-within .tracking-question-imitation-trigger {
  opacity: 1;
  transform: translateY(0);
}

.tracking-question-source-card:hover .tracking-question-imitation-trigger--source,
.tracking-question-source-card:focus-within .tracking-question-imitation-trigger--source {
  opacity: 1;
  transform: translateY(-50%);
}

.tracking-question-imitation-trigger--table-action {
  margin: 0 auto;
  opacity: 1;
  transform: none;
}

.tracking-question-table__empty-action {
  color: #cbd5e1;
  font-size: 12px;
}

.tracking-question-source-card__link {
  color: #94a3b8;
  font-size: 12px;
  width: 100%;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.tracking-question-content-card__copy a {
  color: #2563eb;
  font-size: 16px;
  font-weight: 600;
}

.tracking-question-answer__content :deep(.tracking-inline-citation) {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 28px;
  height: 22px;
  margin-left: 6px;
  padding: 0 8px;
  border: 1px solid #bfdbfe;
  border-radius: 999px;
  background: #eff6ff;
  color: #2563eb;
  font-size: 12px;
  font-weight: 700;
  line-height: 1;
  vertical-align: baseline;
  cursor: pointer;
  text-decoration: none;
  transition: all 0.18s ease;
}

.tracking-question-answer__content :deep(.tracking-inline-citation:hover) {
  border-color: #93c5fd;
  background: #dbeafe;
}

.tracking-question-answer__content :deep(.tracking-inline-citation--muted) {
  cursor: default;
  color: #94a3b8;
  border-color: #e2e8f0;
  background: #f8fafc;
}

/* Modern Enterprise Table Styles */
:deep(.modern-table) {
  width: 100%;
}

:deep(.modern-table .ant-table-thead > tr > th) {
  background-color: transparent !important;
  color: #64748b;
  font-weight: 600;
  font-size: 13px;
  border-bottom: 1px solid #e2e8f0;
  padding: 16px 24px;
}

:deep(.modern-table .ant-table-tbody > tr > td) {
  padding: 16px 24px;
  border-bottom: 1px solid #f1f5f9;
  vertical-align: middle;
  transition: background-color 0.2s ease;
}

:deep(.modern-table .ant-table-tbody > tr:last-child > td) {
  border-bottom: none;
}

:deep(.modern-table .ant-table-tbody > tr:hover > td) {
  background-color: #f8fafc !important;
}

.cell-primary-stack {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.table-link {
  color: #2563eb;
  font-size: 14px;
  font-weight: 600;
  text-decoration: none;
  transition: color 0.2s ease;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  text-overflow: ellipsis;
}

.table-link:hover {
  color: #1d4ed8;
  text-decoration: underline;
}

.table-text {
  color: #0f172a;
  font-size: 14px;
  font-weight: 600;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  text-overflow: ellipsis;
}

.tracking-question-table__secondary {
  color: #64748b;
  font-size: 13px;
  word-break: break-all;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  text-overflow: ellipsis;
}

.meta-subtext {
  color: #94a3b8;
  font-size: 12px;
}

.metric-value {
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
  color: #475569;
  font-weight: 600;
  font-size: 15px;
}

.metric-highlight {
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
  color: #0ea5e9;
  font-weight: 700;
  font-size: 14px;
  background: #f0f9ff;
  padding: 4px 10px;
  border-radius: 8px;
  border: 1px solid #e0f2fe;
}

.tracking-question-card--debug {
  margin-top: 4px;
}

.tracking-task-debug-list {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.tracking-task-debug-card {
  padding: 20px 22px;
  border: 1px solid #edf2f7;
  border-radius: 20px;
  background: linear-gradient(180deg, #fbfdff 0%, #f5f9ff 100%);
}

.tracking-task-debug-card__header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 18px;
}

.tracking-task-debug-card__headline {
  display: flex;
  min-width: 0;
  flex-direction: column;
  gap: 6px;
}

.tracking-task-debug-card__headline strong {
  color: #111827;
  font-size: 16px;
}

.tracking-task-debug-card__headline small {
  color: #94a3b8;
  word-break: break-all;
}

.tracking-task-debug-card__tags {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 8px;
}

.tracking-task-debug-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 14px;
}

.tracking-task-debug-grid__item {
  display: flex;
  min-width: 0;
  flex-direction: column;
  gap: 8px;
  padding: 14px 16px;
  border-radius: 16px;
  background: #ffffff;
}

.tracking-task-debug-grid__item span {
  color: #94a3b8;
  font-size: 12px;
}

.tracking-task-debug-grid__item strong {
  color: #1f2937;
  font-size: 13px;
  line-height: 1.6;
  word-break: break-all;
}

.tracking-task-debug-notes {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 14px;
  margin-top: 14px;
}

.tracking-task-debug-notes__item {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 14px 16px;
  border-radius: 16px;
  background: #ffffff;
}

.tracking-task-debug-notes__item span {
  color: #94a3b8;
  font-size: 12px;
}

.tracking-task-debug-notes__item strong {
  color: #1f2937;
  line-height: 1.6;
  word-break: break-word;
}

.tracking-task-debug-notes__item.is-error {
  background: #fff7f7;
}

.tracking-task-debug-notes__item.is-error strong {
  color: #b91c1c;
}

@media (max-width: 1080px) {
  .tracking-question-grid {
    grid-template-columns: 1fr;
  }

  .tracking-task-debug-grid,
  .tracking-task-debug-notes {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 720px) {
  .tracking-question-page__hero {
    flex-direction: column;
  }

  .tracking-question-page__copy h1 {
    font-size: 26px;
  }

  .tracking-question-card {
    padding: 22px 18px;
  }

  .tracking-question-card__header--split,
  .tracking-task-debug-card__header {
    flex-direction: column;
  }

  .tracking-question-card__hint,
  .tracking-task-debug-card__tags {
    max-width: none;
    text-align: left;
    justify-content: flex-start;
  }

  .tracking-question-table__head,
  .tracking-question-table__row {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .tracking-question-source-card,
  .tracking-question-content-card {
    flex-direction: column;
    align-items: flex-start;
  }

  .tracking-question-source-card__meta {
    align-items: flex-start;
    justify-content: flex-start;
  }

  .tracking-task-debug-grid,
  .tracking-task-debug-notes {
    grid-template-columns: 1fr;
  }
}
</style>
