import * as React from 'react'
import ReactMarkdown, { type Components, defaultUrlTransform } from 'react-markdown'
import rehypeKatex from 'rehype-katex'
import rehypeRaw from 'rehype-raw'
import rehypeSanitize, { defaultSchema } from 'rehype-sanitize'
import remarkBreaks from 'remark-breaks'
import remarkGfm from 'remark-gfm'
import remarkMath from 'remark-math'
import { FileText, Download } from 'lucide-react'
import { cn } from '@multica/ui/lib/utils'
import { CODE_LIGATURE_CLASS } from '@multica/ui/lib/code-style'
import { CodeBlock, InlineCode } from './CodeBlock'
import { isAllowedFileCardHref, preprocessFileCards } from './file-cards'
import { preprocessLinks } from './linkify'
import { preprocessMentionShortcodes } from './mentions'
import 'katex/dist/katex.min.css'
import './markdown.css'

/**
 * Render modes for markdown content:
 *
 * - 'terminal': Raw output with minimal formatting, control chars visible
 *   Best for: Debug output, raw logs, when you want to see exactly what's there
 *
 * - 'minimal': Clean rendering with syntax highlighting but no extra chrome
 *   Best for: Chat messages, inline content, when you want readability without clutter
 *
 * - 'full': Rich rendering with beautiful tables, styled code blocks, proper typography
 *   Best for: Documentation, long-form content, when presentation matters
 */
export type RenderMode = 'terminal' | 'minimal' | 'full'

export interface MarkdownProps {
  children: string
  /**
   * Render mode controlling formatting level
   * @default 'minimal'
   */
  mode?: RenderMode
  className?: string
  /**
   * Message ID for memoization (optional)
   * When provided, memoizes parsed blocks to avoid re-parsing during streaming
   */
  id?: string
  /**
   * Callback when a URL is clicked
   */
  onUrlClick?: (url: string) => void
  /**
   * Callback when a file path is clicked
   */
  onFileClick?: (path: string) => void
  /**
   * Custom renderer for mention links (e.g. mention://issue/UUID).
   * When not provided, mentions render as a simple styled span.
   */
  renderMention?: (props: { type: string; id: string }) => React.ReactNode
  /**
   * CDN hostname for file card detection (e.g. "multica-static.copilothub.ai").
   * When provided, enables file card preprocessing and rendering.
   */
  cdnDomain?: string
  /**
   * Optional override for the image renderer. When provided, replaces the
   * default `<img>` with constrained sizing. The views-package wrapper uses
   * this to inject the unified `<Attachment>` component so chat messages get
   * the same hover toolbar / lightbox / preview-modal treatment as comments.
   */
  renderImage?: (props: { src: string; alt: string }) => React.ReactNode
  /**
   * Optional override for the file-card renderer. When provided, replaces
   * the simplified card chrome (filename + download button) with whatever
   * the caller supplies. Used the same way as `renderImage` to bridge into
   * the views-package `<Attachment>` component.
   */
  renderFileCard?: (props: { href: string; filename: string }) => React.ReactNode
}

// Sanitization schema — extends GitHub defaults to allow code highlighting classes
// and Multica's internal mention/slash protocols.
const sanitizeSchema = {
  ...defaultSchema,
  protocols: {
    ...defaultSchema.protocols,
    href: [...(defaultSchema.protocols?.href ?? []), 'mention', 'slash'],
  },
  attributes: {
    ...defaultSchema.attributes,
    div: [
      ...(defaultSchema.attributes?.div ?? []),
      'dataType',
      'dataHref',
      'dataFilename',
    ],
    code: [
      ...(defaultSchema.attributes?.code ?? []),
      ['className', /^language-/],
      ['className', /^math-/],
      ['className', /^hljs/],
    ],
    img: [
      ...(defaultSchema.attributes?.img ?? []),
      'alt',
    ],
  },
}

/**
 * Custom URL transform that allows Multica internal protocols while keeping
 * the default security for all other URLs.
 */
function urlTransform(url: string): string {
  if (url.startsWith('mention://')) return url
  if (url.startsWith('slash://skill/')) return url
  return defaultUrlTransform(url)
}


// File path detection regex - matches paths starting with /, ~/, or ./
const FILE_PATH_REGEX =
  /^(?:\/|~\/|\.\/)[\w\-./@]+\.(?:ts|tsx|js|jsx|mjs|cjs|md|json|yaml|yml|py|go|rs|css|scss|less|html|htm|txt|log|sh|bash|zsh|swift|kt|java|c|cpp|h|hpp|rb|php|xml|toml|ini|cfg|conf|env|sql|graphql|vue|svelte|astro|prisma)$/i

/**
 * Create custom components based on render mode
 */
function createComponents(
  mode: RenderMode,
  onUrlClick?: (url: string) => void,
  onFileClick?: (path: string) => void,
  renderMention?: (props: { type: string; id: string }) => React.ReactNode,
  renderImage?: (props: { src: string; alt: string }) => React.ReactNode,
  renderFileCard?: (props: { href: string; filename: string }) => React.ReactNode,
): Partial<Components> {
  const baseComponents: Partial<Components> = {
    // FileCard: intercept <div data-type="fileCard"> from preprocessFileCards
    div: ({ node, children, ...props }) => {
      const dataType = node?.properties?.dataType as string | undefined
      if (dataType === 'fileCard') {
        const rawHref = (node?.properties?.dataHref as string) || ''
        const href = isAllowedFileCardHref(rawHref) ? rawHref : ''
        const filename = (node?.properties?.dataFilename as string) || ''
        if (renderFileCard) {
          return <>{renderFileCard({ href, filename })}</>
        }
        return (
          <div className="my-1 flex items-center gap-2 rounded-md border border-border bg-muted/50 px-2.5 py-1 transition-colors hover:bg-muted">
            <FileText className="size-4 shrink-0 text-muted-foreground" />
            <div className="min-w-0 flex-1">
              <p className="truncate text-sm">{filename}</p>
            </div>
            {href && (
              <button
                type="button"
                className="shrink-0 rounded-md p-1 text-muted-foreground transition-colors hover:bg-secondary hover:text-foreground"
                onClick={() => window.open(href, '_blank', 'noopener,noreferrer')}
              >
                <Download className="size-3.5" />
              </button>
            )}
          </div>
        )
      }
      return <div {...props}>{children}</div>
    },
    // Images: render uploaded images with constrained sizing
    img: ({ src, alt }) => {
      if (renderImage) {
        return <>{renderImage({ src: typeof src === 'string' ? src : '', alt: alt ?? '' })}</>
      }
      return (
        <img
          src={src}
          alt={alt ?? ""}
          className="max-w-full h-auto rounded-md my-2"
          loading="lazy"
        />
      )
    },
    // Links: Make clickable with callbacks, or render as mention
    a: ({ href, children }) => {
      // Mention links: mention://member/id, mention://agent/id, mention://issue/id, mention://all/all
      if (href?.startsWith('mention://')) {
        const mentionMatch = href.match(/^mention:\/\/(member|agent|issue|all)\/(.+)$/)
        if (mentionMatch?.[1] && mentionMatch[2]) {
          const type = mentionMatch[1]
          const id = mentionMatch[2]

          if (renderMention) {
            // Let the custom renderer opt out for types it doesn't handle
            // by returning null/undefined — we then fall through to the
            // default styled span so nothing ever disappears silently.
            const rendered = renderMention({ type, id })
            if (rendered) return <>{rendered}</>
          }

          // Fallback: render as a simple styled span
          return (
            <span className="text-primary font-semibold mx-0.5">
              {children}
            </span>
          )
        }
        return (
          <span className="text-primary font-semibold mx-0.5">
            {children}
          </span>
        )
      }

      if (href?.startsWith('slash://skill/')) {
        return (
          <span className="slash-command text-primary font-semibold mx-0.5">
            {children}
          </span>
        )
      }

      const handleClick = (e: React.MouseEvent): void => {
        e.preventDefault()
        if (href) {
          // Check if it's a file path
          if (FILE_PATH_REGEX.test(href) && onFileClick) {
            onFileClick(href)
          } else if (onUrlClick) {
            onUrlClick(href)
          } else {
            // Default: open in new window
            window.open(href, '_blank', 'noopener,noreferrer')
          }
        }
      }

      return (
        <a
          href={href}
          onClick={handleClick}
          className="text-primary hover:underline cursor-pointer"
        >
          {children}
        </a>
      )
    }
  }

  // Terminal mode: minimal formatting
  if (mode === 'terminal') {
    return {
      ...baseComponents,
      // No special code handling - just monospace
      code: ({ children }) => <code className={cn('font-mono', CODE_LIGATURE_CLASS)}>{children}</code>,
      pre: ({ children }) => (
        <pre className={cn('font-mono whitespace-pre-wrap my-2', CODE_LIGATURE_CLASS)}>
          {children}
        </pre>
      ),
      // Minimal paragraph spacing
      p: ({ children }) => <p className="my-1">{children}</p>,
      // Simple lists
      ul: ({ children }) => <ul className="list-disc list-inside my-1">{children}</ul>,
      ol: ({ children }) => <ol className="list-decimal list-inside my-1">{children}</ol>,
      li: ({ children }) => <li className="my-0.5">{children}</li>,
      // Plain tables
      table: ({ children }) => <table className="my-2 font-mono text-sm">{children}</table>,
      th: ({ children }) => <th className="text-left pr-4">{children}</th>,
      td: ({ children }) => <td className="pr-4">{children}</td>
    }
  }

  // Minimal mode: clean with syntax highlighting
  if (mode === 'minimal') {
    return {
      ...baseComponents,
      // Inline code
      code: ({ className, children, ...props }) => {
        const match = /language-(\w+)/.exec(className || '')
        const isBlock =
          'node' in props && props.node?.position?.start.line !== props.node?.position?.end.line

        // Block code - use CodeBlock with full mode
        if (match || isBlock) {
          const code = String(children).replace(/\n$/, '')
          return <CodeBlock code={code} language={match?.[1]} mode="full" className="my-1" />
        }

        // Inline code
        return <InlineCode>{children}</InlineCode>
      },
      pre: ({ children }) => <>{children}</>,
      // Comfortable paragraph spacing
      p: ({ children }) => <p className="my-2 leading-relaxed">{children}</p>,
      // Styled lists
      ul: ({ children }) => (
        <ul className="my-2 space-y-1 ps-4 pe-2 list-disc marker:text-muted-foreground">
          {children}
        </ul>
      ),
      ol: ({ children }) => <ol className="my-2 space-y-1 pl-6 list-decimal">{children}</ol>,
      li: ({ children }) => <li>{children}</li>,
      // Clean tables
      table: ({ children }) => (
        <div className="my-3 overflow-x-auto">
          <table className="min-w-full text-sm">{children}</table>
        </div>
      ),
      thead: ({ children }) => <thead className="border-b">{children}</thead>,
      th: ({ children }) => (
        <th className="text-left py-2 px-3 font-semibold text-muted-foreground">{children}</th>
      ),
      td: ({ children }) => <td className="py-2 px-3 border-b border-border/50">{children}</td>,
      // Headings - H1/H2 same size, differentiated by weight
      h1: ({ children }) => <h1 className="font-sans text-base font-bold mt-5 mb-3">{children}</h1>,
      h2: ({ children }) => (
        <h2 className="font-sans text-base font-semibold mt-4 mb-3">{children}</h2>
      ),
      h3: ({ children }) => (
        <h3 className="font-sans text-sm font-semibold mt-4 mb-2">{children}</h3>
      ),
      // Blockquotes
      blockquote: ({ children }) => (
        <blockquote className="border-l-2 border-muted-foreground/30 pl-3 my-2 text-muted-foreground italic">
          {children}
        </blockquote>
      ),
      // Horizontal rules
      hr: () => <hr className="my-4 border-border" />,
      // Strong/emphasis
      strong: ({ children }) => <strong className="font-semibold">{children}</strong>,
      em: ({ children }) => <em className="italic">{children}</em>
    }
  }

  // Full mode: rich styling
  return {
    ...baseComponents,
    // Full code blocks with copy button
    code: ({ className, children, ...props }) => {
      const match = /language-(\w+)/.exec(className || '')
      const isBlock =
        'node' in props && props.node?.position?.start.line !== props.node?.position?.end.line

      if (match || isBlock) {
        const code = String(children).replace(/\n$/, '')
        return <CodeBlock code={code} language={match?.[1]} mode="full" className="my-1" />
      }

      return <InlineCode>{children}</InlineCode>
    },
    pre: ({ children }) => <>{children}</>,
    // Rich paragraph spacing
    p: ({ children }) => <p className="my-3 leading-relaxed">{children}</p>,
    // Styled lists
    ul: ({ children }) => (
      <ul className="my-3 space-y-1.5 ps-4 pe-2 list-disc marker:text-muted-foreground">
        {children}
      </ul>
    ),
    ol: ({ children }) => <ol className="my-3 space-y-1.5 pl-6 list-decimal">{children}</ol>,
    li: ({ children }) => <li className="leading-relaxed">{children}</li>,
    // Beautiful tables
    table: ({ children }) => (
      <div className="my-4 overflow-x-auto rounded-md border">
        <table className="min-w-full divide-y divide-border">{children}</table>
      </div>
    ),
    thead: ({ children }) => <thead className="bg-muted/50">{children}</thead>,
    tbody: ({ children }) => <tbody className="divide-y divide-border">{children}</tbody>,
    th: ({ children }) => <th className="text-left py-3 px-4 font-semibold text-sm">{children}</th>,
    td: ({ children }) => <td className="py-3 px-4 text-sm">{children}</td>,
    tr: ({ children }) => <tr className="hover:bg-muted/30 transition-colors">{children}</tr>,
    // Rich headings
    h1: ({ children }) => <h1 className="font-sans text-base font-bold mt-7 mb-4">{children}</h1>,
    h2: ({ children }) => (
      <h2 className="font-sans text-base font-semibold mt-6 mb-3">{children}</h2>
    ),
    h3: ({ children }) => <h3 className="font-sans text-sm font-semibold mt-5 mb-3">{children}</h3>,
    h4: ({ children }) => <h4 className="text-sm font-semibold mt-3 mb-1">{children}</h4>,
    // Styled blockquotes
    blockquote: ({ children }) => (
      <blockquote className="border-l-4 border-foreground/30 bg-muted/30 pl-4 pr-3 py-2 my-3 rounded-r-md">
        {children}
      </blockquote>
    ),
    // Task lists (GFM)
    input: ({ type, checked }) => {
      if (type === 'checkbox') {
        return (
          <input
            type="checkbox"
            checked={checked}
            readOnly
            className="mr-2 rounded border-muted-foreground"
          />
        )
      }
      return <input type={type} />
    },
    // Horizontal rules
    hr: () => <hr className="my-6 border-border" />,
    // Strong/emphasis
    strong: ({ children }) => <strong className="font-semibold">{children}</strong>,
    em: ({ children }) => <em className="italic">{children}</em>,
    del: ({ children }) => <del className="line-through text-muted-foreground">{children}</del>
  }
}

/**
 * Markdown - Customizable markdown renderer with multiple render modes
 *
 * Features:
 * - Three render modes: terminal, minimal, full
 * - Syntax highlighting via Shiki
 * - GFM support (tables, task lists, strikethrough)
 * - Clickable links and file paths
 * - Memoization for streaming performance
 * - Pluggable mention rendering via renderMention prop
 */
export function Markdown({
  children,
  mode = 'minimal',
  className,
  onUrlClick,
  onFileClick,
  renderMention,
  renderImage,
  renderFileCard,
  cdnDomain
}: MarkdownProps): React.JSX.Element {
  const components = React.useMemo(
    () => createComponents(mode, onUrlClick, onFileClick, renderMention, renderImage, renderFileCard),
    [mode, onUrlClick, onFileClick, renderMention, renderImage, renderFileCard]
  )

  // Preprocess: convert mention shortcodes, raw URLs, and file cards to renderable content
  const processedContent = React.useMemo(
    () => {
      let result = preprocessMentionShortcodes(children)
      result = preprocessLinks(result)
      result = preprocessFileCards(result, cdnDomain ?? '')
      return result
    },
    [children, cdnDomain]
  )

  return (
    <div className={cn('markdown-content break-words', className)}>
      <ReactMarkdown
        remarkPlugins={[remarkMath, remarkBreaks, [remarkGfm, { singleTilde: false }]]}
        rehypePlugins={[rehypeRaw, [rehypeSanitize, sanitizeSchema], rehypeKatex]}
        urlTransform={urlTransform}
        components={components}
      >
        {processedContent}
      </ReactMarkdown>
    </div>
  )
}

/**
 * MemoizedMarkdown - Optimized for streaming scenarios
 *
 * Splits content into blocks and memoizes each block separately,
 * so only new/changed blocks re-render during streaming.
 */
export const MemoizedMarkdown = React.memo(Markdown, (prevProps, nextProps) => {
  // If id is provided, use it for memoization
  if (prevProps.id && nextProps.id) {
    return (
      prevProps.id === nextProps.id &&
      prevProps.children === nextProps.children &&
      prevProps.mode === nextProps.mode
    )
  }
  // Otherwise compare content and mode
  return prevProps.children === nextProps.children && prevProps.mode === nextProps.mode
})
MemoizedMarkdown.displayName = 'MemoizedMarkdown'
