import Foundation import Observation import TermindDomain import struct TermindDomain.Host @MainActor @Observable public final class ConnectionHistoryModel { public static let limit = 1_000 private let repository: any ConnectionHistoryRepository private var cachedEntries: [ConnectionHistoryEntry]? public private(set) var revision: UInt64 = 0 public private(set) var storageError: String? public init( entries: [ConnectionHistoryEntry] = [], repository: (any ConnectionHistoryRepository)? = nil ) { self.repository = repository ?? InMemoryConnectionHistoryRepository(entries: entries) } public func entries() -> [ConnectionHistoryEntry] { _ = revision if let cachedEntries { return cachedEntries } do { let values = try repository.entries() cachedEntries = values storageError = nil return values } catch { storageError = error.localizedDescription return [] } } public func entries(for hostID: Host.ID) -> [ConnectionHistoryEntry] { entries().filter { $0.hostID == hostID } } public func mostRecentDate(for hostID: Host.ID) -> Date? { entries().first { $0.hostID == hostID }?.connectedAt } @discardableResult public func record( kind: ConnectionHistoryEntry.Kind, for hostID: Host.ID, at date: Date = Date() ) -> ConnectionHistoryEntry? { let entry = ConnectionHistoryEntry(hostID: hostID, kind: kind, connectedAt: date) do { try repository.insert(entry, limit: Self.limit) cachedEntries = nil storageError = nil revision &+= 1 return entry } catch { storageError = error.localizedDescription return nil } } public func delete(_ id: ConnectionHistoryEntry.ID) { do { try repository.delete(id) cachedEntries = nil storageError = nil revision &+= 1 } catch { storageError = error.localizedDescription } } public func clear(for hostID: Host.ID) { do { try repository.clear(for: hostID) cachedEntries = nil storageError = nil revision &+= 1 } catch { storageError = error.localizedDescription } } public func clearAll() { do { try repository.clearAll() cachedEntries = [] storageError = nil revision &+= 1 } catch { storageError = error.localizedDescription } } }