import Foundation
import Models

/// Public SMTP client for sending email messages.
public actor SMTPClient {
	private let host: String
	private let port: Int
	private let security: SMTPSecurity
	private let credentials: Credentials

	public init(host: String, port: Int, security: SMTPSecurity, credentials: Credentials) {
		self.host = host
		self.port = port
		self.security = security
		self.credentials = credentials
	}

	/// Send an outgoing email message through a full SMTP session.
	/// Performs: connect → EHLO → [STARTTLS + re-EHLO] → AUTH → MAIL FROM → RCPT TO → DATA → QUIT
	public func send(message: OutgoingMessage) async throws {
		let connection = SMTPConnection(host: host, port: port, security: security)
		let runner = SMTPCommandRunner(connection: connection)
		let domain = MessageFormatter.domainFromEmail(message.from.address)

		defer {
			Task {
				try? await runner.quit()
				try? await connection.disconnect()
				try? await connection.shutdown()
			}
		}

		_ = try await connection.connect()
		_ = try await runner.ehlo(hostname: domain)

		if security == .starttls {
			try await runner.startTLS()
			_ = try await runner.ehlo(hostname: domain)
		}

		try await runner.authenticate(credentials: credentials)

		try await runner.mailFrom(message.from.address)

		// All recipients: to + cc + bcc
		let allRecipients = message.to + message.cc + message.bcc
		for recipient in allRecipients {
			try await runner.rcptTo(recipient.address)
		}

		let formattedMessage = MessageFormatter.format(message)
		try await runner.data(formattedMessage)
	}

	/// Test the SMTP connection by performing auth without sending.
	/// Performs: connect → EHLO → [STARTTLS + re-EHLO] → AUTH → QUIT
	public func testConnection() async throws {
		let connection = SMTPConnection(host: host, port: port, security: security)
		let runner = SMTPCommandRunner(connection: connection)
		let domain = MessageFormatter.domainFromEmail(credentials.username)

		defer {
			Task {
				try? await runner.quit()
				try? await connection.disconnect()
				try? await connection.shutdown()
			}
		}

		_ = try await connection.connect()
		_ = try await runner.ehlo(hostname: domain)

		if security == .starttls {
			try await runner.startTLS()
			_ = try await runner.ehlo(hostname: domain)
		}

		try await runner.authenticate(credentials: credentials)
	}
}
