import { extname } from 'path'
import React, { Suspense, use, useMemo } from 'react'
import { Ansi, Text } from '@anthropic/ink'
import { LRUCache } from 'lru-cache'
import { getCliHighlightPromise } from '../../utils/cliHighlight.js'
import { logForDebugging } from '../../utils/debug.js'
import { convertLeadingTabsToSpaces } from '../../utils/file.js'
import { hashPair } from '../../utils/hash.js'

type Props = {
  code: string
  filePath: string
  dim?: boolean
  skipColoring?: boolean
}

// Module-level highlight cache — hl.highlight() is the hot cost on virtual-
// scroll remounts. useMemo doesn't survive unmount→remount. Keyed by hash
// of code+language to avoid retaining full source strings (#24180 RSS fix).
const hlCache = new LRUCache<string, string>({ max: 500 })
function cachedHighlight(
  hl: NonNullable<Awaited<ReturnType<typeof getCliHighlightPromise>>>,
  code: string,
  language: string,
): string {
  const key = hashPair(language, code)
  const hit = hlCache.get(key)
  if (hit !== undefined) return hit
  const out = hl.highlight(code, { language })
  hlCache.set(key, out)
  return out
}

export function HighlightedCodeFallback({
  code,
  filePath,
  dim = false,
  skipColoring = false,
}: Props): React.ReactElement {
  const codeWithSpaces = convertLeadingTabsToSpaces(code)
  if (skipColoring) {
    return (
      <Text dimColor={dim}>
        <Ansi>{codeWithSpaces}</Ansi>
      </Text>
    )
  }
  const language = extname(filePath).slice(1)
  return (
    <Text dimColor={dim}>
      <Suspense fallback={<Ansi>{codeWithSpaces}</Ansi>}>
        <Highlighted codeWithSpaces={codeWithSpaces} language={language} />
      </Suspense>
    </Text>
  )
}

function Highlighted({
  codeWithSpaces,
  language,
}: {
  codeWithSpaces: string
  language: string
}): React.ReactElement {
  const hl = use(getCliHighlightPromise())
  const out = useMemo(() => {
    if (!hl) return codeWithSpaces
    let highlightLang = 'markdown'
    if (language) {
      if (hl.supportsLanguage(language)) {
        highlightLang = language
      } else {
        logForDebugging(
          `Language not supported while highlighting code, falling back to markdown: ${language}`,
        )
      }
    }
    try {
      return cachedHighlight(hl, codeWithSpaces, highlightLang)
    } catch (e) {
      if (e instanceof Error && e.message.includes('Unknown language')) {
        logForDebugging(
          `Language not supported while highlighting code, falling back to markdown: ${e}`,
        )
        return cachedHighlight(hl, codeWithSpaces, 'markdown')
      }
      return codeWithSpaces
    }
  }, [codeWithSpaces, language, hl])
  return <Ansi>{out}</Ansi>
}
