import { linkifyHtml } from './linkify'

/**
 * Lightweight message formatting for the ticket chat.
 *
 * Messages stay PLAIN TEXT in iDempiere (r_requestupdate.Result) with
 * markdown-style markers, so every other consumer — list teasers, e-mail
 * notifications, the mobile app, search — keeps showing readable text. Only the
 * chat view renders them:
 *
 *   **bold**            → <strong>
 *   _italic_ / *italic* → <em>
 *   - item / * item     → <ul><li>   (also "• item")
 *   1. item / 1) item   → <ol><li>
 *   http(s)://… / www.… → <a target="_blank"> (via linkifyHtml)
 *   [label](https://…)  → <a target="_blank"> (what the WYSIWYG composer emits)
 *   newline             → <br>, blank line → small paragraph gap
 *
 * Safety: every text segment goes through linkifyHtml, which HTML-escapes it
 * and only injects the <a> tags it builds itself. Inline markers are applied
 * AFTER escaping and never inside those <a> tags, so the result is safe for
 * `v-html` — the only markup emitted is strong/em/ul/ol/li/br/a and the gap div.
 */

// Inline markers, applied to already-escaped HTML (never inside <a>…</a>).
// Boundaries keep `_` inside words/URLs (snake_case, file_name.pdf) untouched.
const BOLD_RE = /\*\*(\S(?:[^*\n]*?\S)?)\*\*/g
const ITALIC_STAR_RE = /(^|[\s(>])\*(\S(?:[^*\n]*?\S)?)\*(?=$|[\s).,!?:;<])/g
const ITALIC_UNDERSCORE_RE = /(^|[\s(>])_(\S(?:[^_\n]*?\S)?)_(?=$|[\s).,!?:;<])/g

const applyInlineMarkers = (html: string): string => html
  .replace(BOLD_RE, '<strong>$1</strong>')
  .replace(ITALIC_STAR_RE, '$1<em>$2</em>')
  .replace(ITALIC_UNDERSCORE_RE, '$1<em>$2</em>')

const escapeHtml = (s: string): string => s
  .replace(/&/g, '&amp;')
  .replace(/</g, '&lt;')
  .replace(/>/g, '&gt;')
  .replace(/"/g, '&quot;')
  .replace(/'/g, '&#39;')

// Markdown links — what the WYSIWYG composer emits for a pasted/auto-linked
// URL ("[label](https://…)"). Bare URLs are handled by linkifyHtml below.
const MD_LINK_RE = /\[([^\]\n]+)\]\((https?:\/\/[^\s)]+)\)/g

// Escape + linkify one segment, then format the non-link parts only.
const inlinePlain = (segment: string): string => linkifyHtml(segment)
  .split(/(<a [^>]*>.*?<\/a>)/)
  .map((part, i) => (i % 2 === 1 ? part : applyInlineMarkers(part)))
  .join('')

const inline = (line: string): string => {
  let out = ''
  let last = 0
  MD_LINK_RE.lastIndex = 0
  let m: RegExpExecArray | null
  while ((m = MD_LINK_RE.exec(line)) !== null) {
    out += inlinePlain(line.slice(last, m.index))
    out += `<a href="${escapeHtml(m[2] ?? '')}" target="_blank" rel="noopener noreferrer">${applyInlineMarkers(escapeHtml(m[1] ?? ''))}</a>`
    last = m.index + m[0].length
  }
  out += inlinePlain(line.slice(last))
  return out
}

const BULLET_RE = /^\s*[-*•]\s+(.*)$/
const NUMBER_RE = /^\s*\d+[.)]\s+(.*)$/

export const formatChatHtml = (input: any): string => {
  const text = input == null ? '' : String(input).replace(/\r\n?/g, '\n')
  const lines = text.split('\n')
  let out = ''
  let list: 'ul' | 'ol' | null = null
  let needBr = false

  for (const raw of lines) {
    const bullet = raw.match(BULLET_RE)
    const number = bullet ? null : raw.match(NUMBER_RE)
    const kind: 'ul' | 'ol' | null = bullet ? 'ul' : number ? 'ol' : null

    if (kind) {
      if (list !== kind) {
        if (list) out += `</${list}>`
        out += `<${kind} class="msg-list">`
        list = kind
      }
      out += `<li>${inline((bullet || number)?.[1] ?? '')}</li>`
      needBr = false
      continue
    }

    if (list) {
      out += `</${list}>`
      list = null
      needBr = false
    }
    // Blank line = paragraph break → a small gap rather than a full empty
    // line, so editor output ("\n\n" between paragraphs) stays compact.
    if (!raw.trim()) {
      out += '<div class="msg-gap"></div>'
      needBr = false
      continue
    }
    if (needBr) out += '<br>'
    out += inline(raw)
    needBr = true
  }

  if (list) out += `</${list}>`
  return out
}

export default formatChatHtml
