import Foundation public protocol SessionBridge: Sendable { func authState() async throws -> AuthState func pollEvents() async throws -> [SessionEvent] func sendPhoneNumber(_ phone: String) async throws func sendCode(_ code: String) async throws func sendPassword(_ password: String) async throws func loadFolders() async throws -> [Folder] func loadChats(folderId: Int32?) async throws -> [ChatSummary] func loadHistory(chatId: Int64) async throws -> [Message] func openProfile(chatId: Int64) async throws -> Profile func sendMessage(chatId: Int64, text: String, replyToMessageId: Int64?) async throws -> Message } public actor FakeSessionBridge: SessionBridge { private var auth: AuthState private var chats: [ChatSummary] private var messages: [Int64: [Message]] private var events: [SessionEvent] private var nextMessageId: Int64 public init(auth: AuthState = .waitPhoneNumber) { self.auth = auth let saved = ChatSummary( id: 1, title: "Saved Messages", username: "saved", lastMessage: "Hello from fake TDLib", unreadCount: 1, isPinned: true ) let team = ChatSummary( id: 2, title: "iOS Team", lastMessage: "Bridge smoke is green", unreadMentionCount: 1, folderIds: [0, 2], isMuted: true, draft: Draft(chatId: 2, text: "Follow up") ) self.chats = [saved, team] self.messages = [ 1: [ Message(id: 1, chatId: 1, senderName: "Alice", text: "Hello from fake TDLib", isOutgoing: false, isRead: false) ], 2: [ Message(id: 2, chatId: 2, senderName: "Mikhail", text: "Bridge smoke is green", isOutgoing: true) ], ] self.events = [.chatListChanged([saved, team])] self.nextMessageId = 3 } public func authState() async throws -> AuthState { auth } public func pollEvents() async throws -> [SessionEvent] { let drained = events events.removeAll() return drained } public func sendPhoneNumber(_ phone: String) async throws { auth = .waitCode events.append(.authChanged(auth)) } public func sendCode(_ code: String) async throws { auth = .waitPassword events.append(.authChanged(auth)) } public func sendPassword(_ password: String) async throws { auth = .ready events.append(.authChanged(auth)) } public func loadFolders() async throws -> [Folder] { [Folder(id: 0, name: "All"), Folder(id: 2, name: "Work")] } public func loadChats(folderId: Int32?) async throws -> [ChatSummary] { let result = folderId.map { folderId in chats.filter { $0.folderIds.contains(folderId) } } ?? chats events.append(.chatListChanged(result)) return result } public func loadHistory(chatId: Int64) async throws -> [Message] { messages[chatId] ?? [] } public func openProfile(chatId: Int64) async throws -> Profile { let chat = chats.first { $0.id == chatId } let profile = Profile( chatId: chatId, title: chat?.title ?? "Unknown", username: chat?.username, bio: chatId == 1 ? "Fake profile for the iOS app shell" : "Team chat", isGroup: chatId != 1, memberCount: chatId == 1 ? nil : 4 ) events.append(.profileLoaded(profile)) return profile } public func sendMessage(chatId: Int64, text: String, replyToMessageId: Int64?) async throws -> Message { let message = Message( id: nextMessageId, chatId: chatId, senderName: "Me", text: text, isOutgoing: true, replyText: replyToMessageId.map { "Reply to #\($0)" } ) nextMessageId += 1 messages[chatId, default: []].append(message) if let index = chats.firstIndex(where: { $0.id == chatId }) { chats[index].lastMessage = text chats[index].draft = nil } events.append(.messageAdded(chatId, message)) return message } }