/**
 * Onboarding SMTP connectivity test — no persistence (Task 6).
 *
 * Performs EHLO + AUTH LOGIN over Workers TCP (cloudflare:sockets).
 * Port 465 → implicit TLS; port 587 → STARTTLS upgrade.
 */
import { connect } from 'cloudflare:sockets'

const TIMEOUT_MS = 10_000
const GENERIC_AUTH_ERROR = 'SMTP authentication failed'
const GENERIC_CONNECTION_ERROR = 'Could not connect to the mail server'

export type SmtpTestInput = {
  host: string
  port: number
  username: string
  password: string
}

type SmtpTestResult = { ok: true } | { ok: false; error: string }

function withTimeout<T>(promise: Promise<T>, ms: number): Promise<T> {
  return new Promise((resolve, reject) => {
    const timer = setTimeout(() => reject(new Error('timeout')), ms)
    promise
      .then((value) => {
        clearTimeout(timer)
        resolve(value)
      })
      .catch((err) => {
        clearTimeout(timer)
        reject(err)
      })
  })
}

async function readLine(reader: ReadableStreamDefaultReader<Uint8Array>): Promise<string> {
  const decoder = new TextDecoder()
  let buffer = ''
  while (true) {
    const { value, done } = await reader.read()
    if (done) break
    buffer += decoder.decode(value, { stream: true })
    const idx = buffer.indexOf('\r\n')
    if (idx !== -1) {
      const line = buffer.slice(0, idx)
      return line
    }
  }
  if (buffer.length > 0) return buffer
  throw new Error('connection closed')
}

async function readMultiline(reader: ReadableStreamDefaultReader<Uint8Array>): Promise<string[]> {
  const lines: string[] = []
  while (true) {
    const line = await readLine(reader)
    lines.push(line)
    if (line.length >= 4 && line[3] === ' ') break
  }
  return lines
}

async function expectCode(
  reader: ReadableStreamDefaultReader<Uint8Array>,
  expected: number,
): Promise<void> {
  const line = await readLine(reader)
  const code = Number.parseInt(line.slice(0, 3), 10)
  if (code !== expected) {
    throw new Error(`unexpected smtp code ${code}`)
  }
}

async function sendLine(
  writer: WritableStreamDefaultWriter<Uint8Array>,
  line: string,
): Promise<void> {
  await writer.write(new TextEncoder().encode(`${line}\r\n`))
}

function toBase64(value: string): string {
  return btoa(value)
}

function ehloAdvertisesStartTls(lines: string[]): boolean {
  return lines.some((line) => {
    const rest = line.slice(4).toUpperCase()
    return line.startsWith('250') && rest.includes('STARTTLS')
  })
}

async function runSmtpAuth(input: SmtpTestInput): Promise<void> {
  const implicitTls = input.port === 465
  let encrypted = implicitTls
  let socket = connect(
    { hostname: input.host, port: input.port },
    { secureTransport: implicitTls ? 'on' : 'off', allowHalfOpen: false },
  )

  let writer = socket.writable.getWriter()
  let reader = socket.readable.getReader()

  await expectCode(reader, 220)
  await sendLine(writer, 'EHLO zync.is')
  const ehloLines = await readMultiline(reader)

  if (!encrypted && ehloAdvertisesStartTls(ehloLines)) {
    await sendLine(writer, 'STARTTLS')
    await expectCode(reader, 220)
    socket = socket.startTls()
    writer = socket.writable.getWriter()
    reader = socket.readable.getReader()
    encrypted = true
    await sendLine(writer, 'EHLO zync.is')
    await readMultiline(reader)
  }

  if (!encrypted) {
    throw new Error('smtp channel not encrypted')
  }

  await sendLine(writer, 'AUTH LOGIN')
  await expectCode(reader, 334)
  await sendLine(writer, toBase64(input.username))
  await expectCode(reader, 334)
  await sendLine(writer, toBase64(input.password))
  await expectCode(reader, 235)

  try {
    await sendLine(writer, 'QUIT')
  } catch {
    // best-effort shutdown
  }

  try {
    await writer.close()
  } catch {
    // ignore
  }
}

export async function testSmtpConnection(input: SmtpTestInput): Promise<SmtpTestResult> {
  try {
    await withTimeout(runSmtpAuth(input), TIMEOUT_MS)
    return { ok: true }
  } catch (err) {
    const message = err instanceof Error ? err.message : 'unknown'
    if (message.includes('unexpected smtp code 535') || message.includes('unexpected smtp code 534')) {
      return { ok: false, error: GENERIC_AUTH_ERROR }
    }
    if (
      message === 'timeout' ||
      message.includes('connection closed') ||
      message === 'smtp channel not encrypted'
    ) {
      return { ok: false, error: GENERIC_CONNECTION_ERROR }
    }
    if (message.startsWith('unexpected smtp code')) {
      return { ok: false, error: GENERIC_AUTH_ERROR }
    }
    return { ok: false, error: GENERIC_CONNECTION_ERROR }
  }
}
