const UNSUBSCRIBE_TERM =
  /unsubscribe|désabonner|desabonner|préférences|preferences/i

/** Matches href paths like /unsub, ?unsub=, List-Unsubscribe URLs, etc. */
const UNSUBSCRIBE_HREF = /unsub/i

export type UnsubscribeMailto = {
  address: string
  subject?: string
  body?: string
}

export type UnsubscribeAction =
  | { kind: "mailto"; mailto: UnsubscribeMailto }
  | { kind: "http"; url: string }

function isUnsubscribeHref(href: string): boolean {
  const h = href.trim()
  if (!h) return false
  return UNSUBSCRIBE_HREF.test(h) || UNSUBSCRIBE_TERM.test(h)
}

function unwrapAngle(s: string): string {
  const t = s.trim()
  if (t.startsWith("<") && t.endsWith(">")) return t.slice(1, -1).trim()
  return t
}

function splitListHeaderParts(raw: string): string[] {
  const parts: string[] = []
  let cur = ""
  let depth = 0
  for (const ch of raw) {
    if (ch === "<") depth++
    else if (ch === ">") depth = Math.max(0, depth - 1)
    if (ch === "," && depth === 0) {
      if (cur.trim()) parts.push(cur.trim())
      cur = ""
    } else {
      cur += ch
    }
  }
  if (cur.trim()) parts.push(cur.trim())
  return parts
}

function parseMailtoUrl(raw: string): UnsubscribeMailto | null {
  const unwrapped = unwrapAngle(raw)
  if (!/^mailto:/i.test(unwrapped)) return null
  try {
    const u = new URL(unwrapped)
    const pathAddr = decodeURIComponent(u.pathname.replace(/^\/+/, ""))
    const address =
      pathAddr.includes("@")
        ? pathAddr
        : decodeURIComponent(unwrapped.replace(/^mailto:/i, "").split("?")[0] ?? "")
    if (!address.includes("@")) return null
    return {
      address,
      subject: u.searchParams.get("subject") ?? undefined,
      body: u.searchParams.get("body") ?? undefined,
    }
  } catch {
    const addr = unwrapped.replace(/^mailto:/i, "").split("?")[0]?.trim()
    if (!addr?.includes("@")) return null
    return { address: addr }
  }
}

function parseListUnsubscribeHeader(raw: string): {
  mailto: UnsubscribeMailto | null
  http: string | null
} {
  let mailto: UnsubscribeMailto | null = null
  let http: string | null = null
  for (const part of splitListHeaderParts(raw)) {
    const p = unwrapAngle(part)
    const m = parseMailtoUrl(p)
    if (m && !mailto) {
      mailto = m
      continue
    }
    if (/^https?:\/\//i.test(p) && !http) {
      http = p
    }
  }
  return { mailto, http }
}

function firstHttpUrl(raw: string): string | null {
  const fromHeader = parseListUnsubscribeHeader(raw).http
  if (fromHeader) return fromHeader
  const angle = raw.match(/<https?:\/\/[^>]+>/i)
  if (angle) return angle[0]!.slice(1, -1)
  const plain = raw.match(/https?:\/\/[^\s>,]+/i)
  return plain?.[0] ?? null
}

function mailtoFromHtml(html: string): UnsubscribeMailto | null {
  const re =
    /<a\b[^>]*href=["']([^"']+)["'][^>]*>([\s\S]*?)<\/a>/gi
  let m: RegExpExecArray | null
  while ((m = re.exec(html)) !== null) {
    const href = m[1] ?? ""
    const text = m[2] ?? ""
    const mailto = parseMailtoUrl(href)
    if (mailto && (UNSUBSCRIBE_TERM.test(text) || isUnsubscribeHref(href))) {
      return mailto
    }
  }
  return null
}

function httpFromHtml(html: string): string | null {
  const re =
    /<a\b[^>]*href=["']([^"']+)["'][^>]*>([\s\S]*?)<\/a>/gi
  let m: RegExpExecArray | null
  while ((m = re.exec(html)) !== null) {
    const href = m[1] ?? ""
    const text = m[2] ?? ""
    if (/^https?:\/\//i.test(href) && (isUnsubscribeHref(href) || UNSUBSCRIBE_TERM.test(text))) {
      return href
    }
  }
  return null
}

function httpFromText(text: string): string | null {
  const re = /https?:\/\/[^\s)]+/gi
  let m: RegExpExecArray | null
  while ((m = re.exec(text)) !== null) {
    const url = m[0]!
    const start = Math.max(0, (m.index ?? 0) - 40)
    const ctx = text.slice(start, (m.index ?? 0) + url.length + 40)
    if (UNSUBSCRIBE_HREF.test(url) || UNSUBSCRIBE_TERM.test(ctx)) return url
  }
  return null
}

/** Resolves unsubscribe: mailto List-Unsubscribe preferred, then https, then body links. */
export function findUnsubscribeAction(
  bodyHtml?: string,
  bodyText?: string,
  listUnsubscribeHeader?: string
): UnsubscribeAction | null {
  if (listUnsubscribeHeader?.trim()) {
    const { mailto, http } = parseListUnsubscribeHeader(listUnsubscribeHeader)
    if (mailto) return { kind: "mailto", mailto }
    if (http) return { kind: "http", url: http }
  }

  const html = bodyHtml?.trim() ?? ""
  const text = bodyText?.trim() ?? ""

  const htmlMailto = mailtoFromHtml(html)
  if (htmlMailto) return { kind: "mailto", mailto: htmlMailto }

  const htmlHttp = httpFromHtml(html)
  if (htmlHttp) return { kind: "http", url: htmlHttp }

  return httpFromText(text) ? { kind: "http", url: httpFromText(text)! } : null
}

/** @deprecated Use findUnsubscribeAction */
export function findUnsubscribeHref(
  bodyHtml?: string,
  bodyText?: string,
  listUnsubscribeHeader?: string
): string | null {
  const action = findUnsubscribeAction(bodyHtml, bodyText, listUnsubscribeHeader)
  if (!action) return null
  if (action.kind === "http") return action.url
  const { address, subject, body } = action.mailto
  const q = new URLSearchParams()
  if (subject) q.set("subject", subject)
  if (body) q.set("body", body)
  const qs = q.toString()
  return `mailto:${address}${qs ? `?${qs}` : ""}`
}
