import Foundation
import Models

/// Builds RFC 5322 formatted email messages.
public enum MessageFormatter: Sendable {

	/// Format an OutgoingMessage into a complete RFC 5322 message string with CRLF line endings.
	public static func format(_ message: OutgoingMessage) -> String {
		var headers: [(String, String)] = []

		headers.append(("From", formatAddress(message.from)))
		headers.append(("To", message.to.map(formatAddress).joined(separator: ", ")))

		if !message.cc.isEmpty {
			headers.append(("Cc", message.cc.map(formatAddress).joined(separator: ", ")))
		}

		// BCC intentionally omitted from headers per RFC 5322

		headers.append(("Subject", message.subject))
		headers.append(("Date", formatRFC2822Date(Date())))
		headers.append(("Message-ID", "<\(message.messageId)>"))

		if let inReplyTo = message.inReplyTo {
			headers.append(("In-Reply-To", "<\(inReplyTo)>"))
		}
		if let references = message.references {
			headers.append(("References", references))
		}

		headers.append(("MIME-Version", "1.0"))
		headers.append(("Content-Type", "text/plain; charset=utf-8"))
		headers.append(("Content-Transfer-Encoding", "quoted-printable"))

		var result = ""
		for (name, value) in headers {
			result += "\(name): \(value)\r\n"
		}
		result += "\r\n"
		result += quotedPrintableEncode(message.bodyText)

		return result
	}

	/// Generate a unique Message-ID for the given domain.
	public static func generateMessageId(domain: String) -> String {
		"\(UUID().uuidString)@\(domain)"
	}

	/// Extract the domain part from an email address.
	public static func domainFromEmail(_ email: String) -> String {
		guard let atIndex = email.lastIndex(of: "@") else { return email }
		return String(email[email.index(after: atIndex)...])
	}

	/// Format an EmailAddress as RFC 5322 address: `"Name" <addr>` or bare `addr`.
	public static func formatAddress(_ addr: EmailAddress) -> String {
		if let name = addr.name, !name.isEmpty {
			return "\"\(name)\" <\(addr.address)>"
		}
		return addr.address
	}

	/// Format a Date as RFC 2822 date string.
	public static func formatRFC2822Date(_ date: Date) -> String {
		let formatter = DateFormatter()
		formatter.locale = Locale(identifier: "en_US_POSIX")
		formatter.dateFormat = "EEE, dd MMM yyyy HH:mm:ss Z"
		formatter.timeZone = TimeZone.current
		return formatter.string(from: date)
	}

	/// Encode a string using quoted-printable encoding (RFC 2045).
	public static func quotedPrintableEncode(_ text: String) -> String {
		var result = ""
		let data = Array(text.utf8)
		var lineLength = 0

		for byte in data {
			let encoded: String
			if byte == 0x0A {
				// LF → CRLF
				encoded = "\r\n"
				result += encoded
				lineLength = 0
				continue
			} else if byte == 0x0D {
				// CR — skip, we handle LF → CRLF above
				continue
			} else if byte == 0x09 || (byte >= 0x20 && byte <= 0x7E && byte != 0x3D) {
				// Printable ASCII (except =) and tab: literal
				encoded = String(UnicodeScalar(byte))
			} else {
				// Everything else: =XX hex encoding
				encoded = String(format: "=%02X", byte)
			}

			// Soft line break if line would exceed 76 chars
			if lineLength + encoded.count > 76 {
				result += "=\r\n"
				lineLength = 0
			}

			result += encoded
			lineLength += encoded.count
		}

		return result
	}
}
