import tls from "tls"

type SmtpConfig = {
  host: string
  port: number
  username: string
  password: string
  from: string
  tls?: boolean
  rejectUnauthorized?: boolean
  timeoutMs?: number
}

function b64(input: string) {
  return Buffer.from(input, "utf8").toString("base64")
}

function extractEnvelopeAddress(from: string): string {
  // Prefer address inside angle brackets
  const angle = from.match(/<\s*([^>\s]+)\s*>/)
  if (angle?.[1]) return angle[1]
  // Fallback: address inside parentheses
  const paren = from.match(/\(([^)\s]+@[^)\s]+)\)/)
  if (paren?.[1]) return paren[1]
  // Fallback: first email-like substring
  const email = from.match(/[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/)
  if (email?.[0]) return email[0]
  // Last resort: use whole string
  return from
}

export async function sendSmtpMail(cfg: SmtpConfig, to: string | string[], subject: string, html: string) {
  return new Promise<void>((resolve, reject) => {
    const socket = tls.connect(cfg.port, cfg.host, { rejectUnauthorized: cfg.rejectUnauthorized ?? false }, () => {
      let buffer = ""
      const send = (line: string) => socket.write(line + "\r\n")
      const wait = (expected: string | RegExp) =>
        new Promise<void>((res, rej) => {
          const timeout = setTimeout(() => {
            socket.removeListener("data", onData)
            rej(new Error("smtp_timeout"))
          }, Math.max(1000, cfg.timeoutMs ?? 10000))
          const onData = (data: Buffer) => {
            buffer += data.toString()
            const lines = buffer.split(/\r?\n/)
            const last = lines.filter(Boolean).slice(-1)[0] ?? ""
            if (typeof expected === "string" ? last.startsWith(expected) : expected.test(last)) {
              socket.removeListener("data", onData)
              clearTimeout(timeout)
              res()
            }
          }
          socket.on("data", onData)
          socket.on("error", (e) => {
            clearTimeout(timeout)
            rej(e)
          })
        })

      ;(async () => {
        await wait(/^220 /)
        send(`EHLO ${cfg.host}`)
        await wait(/^250-/)
        await wait(/^250 /)
        send("AUTH LOGIN")
        await wait(/^334 /)
        send(b64(cfg.username))
        await wait(/^334 /)
        send(b64(cfg.password))
        await wait(/^235 /)
        const envelopeFrom = extractEnvelopeAddress(cfg.from)
        send(`MAIL FROM:<${envelopeFrom}>`)
        await wait(/^250 /)
        const rcpts: string[] = Array.isArray(to)
          ? to
          : String(to)
              .split(/[;,]/)
              .map((s) => s.trim())
              .filter(Boolean)
        for (const rcpt of rcpts) {
          send(`RCPT TO:<${rcpt}>`)
          await wait(/^250 /)
        }
        send("DATA")
        await wait(/^354 /)
        const headers = [
          `From: ${cfg.from}`,
          `To: ${Array.isArray(to) ? to.join(", ") : to}`,
          `Subject: ${subject}`,
          "MIME-Version: 1.0",
          "Content-Type: text/html; charset=UTF-8",
        ].join("\r\n")
        send(headers + "\r\n\r\n" + html + "\r\n.")
        await wait(/^250 /)
        send("QUIT")
        socket.end()
        resolve()
      })().catch(reject)
    })
    socket.on("error", reject)
  })
}
