/** Detect and split quoted reply tails from message HTML (thread view). */

const WROTE_LINE =
  /(^|\n)\s*(Le\s.+a\sécrit\s*:|On\s.+wrote:|Am\s.+schrieb|El\s.+escribió|Il\s.+ha\s+scritto|.+a écrit\s*:)/i

/** Inline reply header after whitespace collapse (no leading newline). */
const WROTE_INLINE =
  /(?:^|[\s.])(Le\s.+?a\s+écrit\s*:|On\s+.+?\bwrote:|Am\s+.+?\bschrieb|El\s+.+?\bescribió|Il\s+.+?\bha\s+scritto|.+?\ba\s+écrit\s*:)/i

const QUOTE_SELECTOR = [
  ".gmail_quote",
  ".gmail_extra",
  ".protonmail_quote",
  ".yahoo_quoted",
  ".moz-cite-prefix",
  "#divRplyFwdMsg",
  'blockquote[type="cite"]',
  "blockquote.gmail_quote",
].join(",")

function wroteLineText(text: string | null | undefined): boolean {
  if (!text?.trim()) return false
  return WROTE_LINE.test(text.trim())
}

function isLikelyReplyBlockquote(el: Element): boolean {
  if (el.tagName !== "BLOCKQUOTE") return false
  const type = el.getAttribute("type")?.toLowerCase()
  if (type === "cite") return true
  const cls = el.className?.toString().toLowerCase() ?? ""
  if (cls.includes("gmail_quote") || cls.includes("protonmail")) return true
  const prev = el.previousElementSibling
  if (prev && wroteLineText(prev.textContent)) return true
  return false
}

function findQuoteElement(root: Element): Element | null {
  for (const sel of QUOTE_SELECTOR.split(",")) {
    const hit = root.querySelector(sel.trim())
    if (hit) return hit
  }
  for (const bq of root.querySelectorAll("blockquote")) {
    if (isLikelyReplyBlockquote(bq)) return bq
  }
  for (const child of root.children) {
    if (wroteLineText(child.textContent) && child.textContent!.length < 500) {
      const next = child.nextElementSibling
      if (next && (next.tagName === "BLOCKQUOTE" || next.matches(QUOTE_SELECTOR))) {
        return child
      }
    }
  }
  return null
}

function quoteSplitStart(quoteEl: Element): Element {
  const prev = quoteEl.previousElementSibling
  if (prev && (wroteLineText(prev.textContent) || prev.classList.contains("gmail_attr"))) {
    return prev
  }
  return quoteEl
}

function fragmentHtml(doc: Document, frag: DocumentFragment): string {
  const wrap = doc.createElement("div")
  wrap.appendChild(frag)
  return wrap.innerHTML.trim()
}

/** Split root contents at quote node (works when quote is nested in a single wrapper div). */
function splitAtQuote(root: Element, quoteEl: Element): {
  mainHtml: string
  quotedHtml: string | null
} {
  const doc = root.ownerDocument
  if (!doc || !root.firstChild) {
    return { mainHtml: root.innerHTML, quotedHtml: null }
  }

  const start = quoteSplitStart(quoteEl)
  try {
    const range = doc.createRange()
    range.setStartBefore(root.firstChild)
    range.setEndBefore(start)
    const mainFrag = range.cloneContents()

    range.setStartBefore(start)
    const last = root.lastChild
    if (!last) return { mainHtml: root.innerHTML, quotedHtml: null }
    range.setEndAfter(last)
    const quotedFrag = range.cloneContents()

    const mainHtml = fragmentHtml(doc, mainFrag)
    const quotedHtml = fragmentHtml(doc, quotedFrag)
    if (!quotedHtml) return { mainHtml: root.innerHTML, quotedHtml: null }
    if (!mainHtml) return { mainHtml: root.innerHTML, quotedHtml: null }
    return { mainHtml, quotedHtml }
  } catch {
    return { mainHtml: root.innerHTML, quotedHtml: null }
  }
}

function splitHtmlRoot(root: Element): { mainHtml: string; quotedHtml: string | null } {
  const quoteHit = findQuoteElement(root)
  if (!quoteHit) {
    return { mainHtml: root.innerHTML, quotedHtml: null }
  }
  return splitAtQuote(root, quoteHit)
}

/** Restore line-based quotes after preview repair collapsed newlines to spaces. */
function normalizeCollapsedPlainQuotes(text: string): string {
  let s = text.replace(/ > /g, "\n> ")
  const wroteIdx = findInlineWroteIndex(s)
  if (wroteIdx > 0) {
    const before = s.slice(0, wroteIdx).trimEnd()
    const after = s.slice(wroteIdx).trimStart()
    if (before && after) s = `${before}\n\n${after}`
  }
  return s
}

function findInlineWroteIndex(text: string): number {
  const m = WROTE_INLINE.exec(text)
  if (!m || m.index === undefined) return -1
  let idx = m.index
  const lead = m[0]![0]
  if (lead && /\s/.test(lead)) idx += 1
  return idx
}

function splitPlainPreBody(html: string): { mainHtml: string; quotedHtml: string | null } {
  const preMatch = html.match(/^<pre[^>]*>([\s\S]*)<\/pre>$/i)
  if (!preMatch) return { mainHtml: html, quotedHtml: null }
  const text = preMatch[1]!
  const decoded = normalizeCollapsedPlainQuotes(
    text
      .replace(/&lt;/g, "<")
      .replace(/&gt;/g, ">")
      .replace(/&amp;/g, "&")
  )
  const lines = decoded.split("\n")
  let splitAt = -1
  for (let i = 0; i < lines.length; i++) {
    const line = lines[i]!
    if (/^>/.test(line.trim())) {
      splitAt = i
      break
    }
    if (WROTE_LINE.test(line)) {
      splitAt = i
      break
    }
  }
  if (splitAt < 0) {
    const inlineIdx = findInlineWroteIndex(decoded)
    if (inlineIdx < 0) return { mainHtml: html, quotedHtml: null }
    const main = decoded.slice(0, inlineIdx).trimEnd()
    const quoted = decoded.slice(inlineIdx).trimStart()
    if (!quoted) return { mainHtml: html, quotedHtml: null }
    const escape = (s: string) =>
      s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")
    const wrap = (body: string) =>
      `<pre style="white-space:pre-wrap;font-family:inherit;margin:0;">${body}</pre>`
    return {
      mainHtml: wrap(escape(main)),
      quotedHtml: wrap(escape(quoted)),
    }
  }

  const mainLines = lines.slice(0, splitAt)
  const quotedLines = lines.slice(splitAt)
  const escape = (s: string) =>
    s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")
  const mainPre = escape(mainLines.join("\n").trimEnd())
  const quotedPre = escape(quotedLines.join("\n").trimStart())
  if (!quotedPre.trim()) return { mainHtml: html, quotedHtml: null }

  const wrap = (body: string) =>
    `<pre style="white-space:pre-wrap;font-family:inherit;margin:0;">${body}</pre>`

  return {
    mainHtml: wrap(mainPre),
    quotedHtml: wrap(quotedPre),
  }
}

export function splitQuotedHtml(html: string): {
  mainHtml: string
  quotedHtml: string | null
} {
  const trimmed = html.trim()
  if (!trimmed) return { mainHtml: html, quotedHtml: null }

  if (/^<pre\b/i.test(trimmed) || /<pre\b/i.test(trimmed)) {
    return splitPlainPreBody(trimmed)
  }

  if (typeof DOMParser === "undefined") {
    return { mainHtml: html, quotedHtml: null }
  }

  const doc = new DOMParser().parseFromString(
    `<div id="mail-body-root">${trimmed}</div>`,
    "text/html"
  )
  const root = doc.getElementById("mail-body-root")
  if (!root) return { mainHtml: html, quotedHtml: null }

  return splitHtmlRoot(root)
}

export function splitPlainTextBody(html: string): { mainHtml: string; quotedHtml: string | null } {
  return splitPlainPreBody(html)
}

export function hasQuotedContent(html: string): boolean {
  return splitQuotedHtml(html).quotedHtml !== null
}
