import Foundation
import IMAPClient
import Models

final class MockIMAPClient: IMAPClientProtocol, @unchecked Sendable {
	var mailboxes: [IMAPMailboxInfo] = []
	var mailboxStatuses: [String: IMAPMailboxStatus] = [:]
	var envelopes: [FetchedEnvelope] = []
	/// Per-mailbox envelopes — takes precedence over flat `envelopes` when set
	var mailboxEnvelopes: [String: [FetchedEnvelope]] = [:]
	var flagUpdates: [UIDFlagsPair] = []
	var bodies: [Int: (text: String?, html: String?)] = [:]

	var connectCalled = false
	var disconnectCalled = false
	var selectedMailbox: String?

	// v0.3 write operation tracking
	var storedFlags: [(uid: Int, mailbox: String, add: [String], remove: [String])] = []
	var movedMessages: [(uid: Int, from: String, to: String)] = []
	var copiedMessages: [(uid: Int, from: String, to: String)] = []
	var expungedMailboxes: [String] = []
	var appendedMessages: [(mailbox: String, message: Data, flags: [String])] = []
	var serverCapabilities: Set<String> = ["IMAP4rev1", "MOVE"]

	func connect() async throws {
		connectCalled = true
	}

	func disconnect() async throws {
		disconnectCalled = true
	}

	func listMailboxes() async throws -> [IMAPMailboxInfo] {
		mailboxes
	}

	func selectMailbox(_ name: String) async throws -> IMAPMailboxStatus {
		selectedMailbox = name
		guard let status = mailboxStatuses[name] else {
			throw MockIMAPError.mailboxNotFound(name)
		}
		return status
	}

	func fetchEnvelopes(uidsGreaterThan uid: Int) async throws -> [FetchedEnvelope] {
		let source: [FetchedEnvelope]
		if let mailbox = selectedMailbox, let perMailbox = mailboxEnvelopes[mailbox] {
			source = perMailbox
		} else {
			source = envelopes
		}
		return source.filter { $0.uid > uid }
	}

	func fetchFlags(uids: ClosedRange<Int>) async throws -> [UIDFlagsPair] {
		flagUpdates.filter { uids.contains($0.uid) }
	}

	func fetchBody(uid: Int) async throws -> (text: String?, html: String?) {
		bodies[uid] ?? (nil, nil)
	}

	// MARK: - v0.3 write operations

	func storeFlags(uid: Int, mailbox: String, add: [String], remove: [String]) async throws {
		storedFlags.append((uid: uid, mailbox: mailbox, add: add, remove: remove))
	}

	func moveMessage(uid: Int, from: String, to: String) async throws {
		movedMessages.append((uid: uid, from: from, to: to))
	}

	func copyMessage(uid: Int, from: String, to: String) async throws {
		copiedMessages.append((uid: uid, from: from, to: to))
	}

	func expunge(mailbox: String) async throws {
		expungedMailboxes.append(mailbox)
	}

	func appendMessage(to mailbox: String, message: Data, flags: [String]) async throws {
		appendedMessages.append((mailbox: mailbox, message: message, flags: flags))
	}

	func capabilities() async throws -> Set<String> {
		serverCapabilities
	}
}

enum MockIMAPError: Error {
	case mailboxNotFound(String)
}
