'use client'

import * as React from 'react'
import { cn } from '../../lib/utils'

const ENTITIES: Record<string, string> = {
  '&amp;': '&',
  '&lt;': '<',
  '&gt;': '>',
  '&quot;': '"',
  '&#39;': "'",
  '&nbsp;': ' ',
}

/**
 * Flatten markdown / HTML / whitespace-noisy text into a single-line preview,
 * clamped to `maxChars` on a word boundary. Board cards get a readable content
 * snippet without pulling in a full markdown renderer (startsim-768w.17.3): the
 * pipeline writes long article bodies to `data.content`, and a raw slice of that
 * shows heading hashes, link syntax and code fences.
 */
export function toPlainSnippet(
  input: string | null | undefined,
  maxChars = 240,
): string {
  if (!input) return ''
  let s = String(input)
  s = s.replace(/```[\s\S]*?```/g, ' ') // fenced code blocks
  s = s.replace(/!\[([^\]]*)\]\([^)]*\)/g, '$1') // images -> alt text
  s = s.replace(/\[([^\]]*)\]\([^)]*\)/g, '$1') // links -> link text
  s = s.replace(/<[^>]+>/g, ' ') // html tags
  s = s.replace(/^[ \t]*[#>\-*+]+[ \t]+/gm, ' ') // heading / list / quote markers
  s = s.replace(/[*_`~]+/g, '') // inline emphasis / code marks
  s = s.replace(/&[a-z#0-9]+;/gi, (m) => ENTITIES[m.toLowerCase()] ?? ' ')
  s = s.replace(/\s+/g, ' ').trim()
  if (s.length <= maxChars) return s
  const cut = s.slice(0, maxChars)
  const lastSpace = cut.lastIndexOf(' ')
  // cut back to the last whole word when that boundary is in the back half,
  // otherwise hard-cut (e.g. a single very long token has no usable boundary)
  const body = lastSpace > maxChars * 0.5 ? cut.slice(0, lastSpace) : cut
  return body.trimEnd() + '…'
}

const LINE_CLAMP: Record<number, string> = {
  2: 'line-clamp-2',
  3: 'line-clamp-3',
  4: 'line-clamp-4',
  5: 'line-clamp-5',
  6: 'line-clamp-6',
}

export interface BoardCardMeta {
  label: string
  value: React.ReactNode
}

export interface BoardCardProps {
  /** Card heading (record name / title). */
  title: React.ReactNode
  /** Raw content (markdown / HTML / plain); shown as a clamped plain-text preview. */
  snippet?: string | null
  /** Max plain-text characters for the snippet (default 240). */
  snippetMaxChars?: number
  /** Tailwind line-clamp count for the snippet (default 3). */
  snippetLines?: 2 | 3 | 4 | 5 | 6
  /** Source / host label or link, shown under the snippet. */
  source?: React.ReactNode
  /** Compact key/value chips for short declared attributes. */
  meta?: BoardCardMeta[]
  /** Opens the record detail; wraps the title as a button when provided. */
  onOpen?: () => void
  /**
   * Footer slot — e.g. a status `<Select>`. Pointer/click events inside are
   * stopped so interacting with a control never starts a drag on the card.
   */
  footer?: React.ReactNode
  className?: string
}

/**
 * A content-aware board card: title + readable content snippet + source + a few
 * meta chips + a footer slot. Presentation-only and drag-agnostic — the
 * KanbanBoard wraps it in a Draggable; this just renders the content the epic's
 * News Item board was missing (title-only cards). Reusable for any entity board.
 */
export function BoardCard({
  title,
  snippet,
  snippetMaxChars = 240,
  snippetLines = 3,
  source,
  meta,
  onOpen,
  footer,
  className,
}: BoardCardProps) {
  const preview = toPlainSnippet(snippet, snippetMaxChars)
  const hasSource = source != null && source !== ''
  return (
    <div
      className={cn(
        'm-2 rounded-md border bg-white p-3 shadow-sm dark:bg-neutral-900',
        className,
      )}
    >
      {onOpen ? (
        <button
          type="button"
          onClick={onOpen}
          className="block w-full text-left text-sm font-medium leading-snug hover:underline"
        >
          {title}
        </button>
      ) : (
        <div className="text-sm font-medium leading-snug">{title}</div>
      )}

      {preview && (
        <p
          className={cn(
            'mt-1 text-xs leading-snug text-neutral-600 dark:text-neutral-300',
            LINE_CLAMP[snippetLines],
          )}
        >
          {preview}
        </p>
      )}

      {hasSource && (
        <div className="mt-1.5 truncate text-xs text-neutral-400">{source}</div>
      )}

      {meta && meta.length > 0 && (
        <dl className="mt-1 space-y-0.5">
          {meta.map((m, i) => (
            <div key={i} className="flex gap-1 text-xs text-neutral-600 dark:text-neutral-300">
              <dt className="capitalize text-neutral-400">{m.label}:</dt>
              <dd className="truncate">{m.value}</dd>
            </div>
          ))}
        </dl>
      )}

      {footer != null && (
        <div
          className="mt-2"
          onClick={(e) => e.stopPropagation()}
          onPointerDown={(e) => e.stopPropagation()}
        >
          {footer}
        </div>
      )}
    </div>
  )
}
