import Foundation import LCEssentials /// Protocol seam for DI, mirroring `FeatureControlEvaluating`. public protocol FeatureControlNotifying: Sendable { func list(status: FeatureControlNotificationStatus, limit: Int) async throws -> (items: [FeatureControlNotification], nextCursor: String?) func markRead(id: String) async throws func markAllRead() async throws } /// JWT-only — the OpenAPI fragment declares `security: [bearerAuth]` for every /// notifications route, no module-token alternative. Configure `configuration.auth` /// with `FeatureControlBearerAuth`; anything else gets a `401` from the server, /// surfaced as `FeatureControlError.unauthorized`. public actor FeatureControlNotificationsClient: FeatureControlNotifying { private let configuration: FeatureControlConfiguration private let api: API public init(configuration: FeatureControlConfiguration, api: API = .shared) { self.configuration = configuration self.api = api } public func list(status: FeatureControlNotificationStatus = .all, limit: Int = 20) async throws -> (items: [FeatureControlNotification], nextCursor: String?) { var headers: [String: String] = [:] await configuration.auth.authorize(&headers) let query = "?status=\(status.rawValue)&limit=\(limit)" do { let envelope: FeatureControlNotificationListEnvelope = try await api.request( url: configuration.baseURL + configuration.notificationsPath + query, method: .get, headers: headers, timeoutInterval: configuration.requestTimeout ) return (envelope.result.items, envelope.result.nextCursor) } catch { throw FeatureControlErrorMapper.map(error) } } public func markRead(id: String) async throws { var headers: [String: String] = [:] await configuration.auth.authorize(&headers) let encodedId = id.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed) ?? id do { let _: String = try await api.request( url: configuration.baseURL + configuration.notificationsPath + "/\(encodedId)/read", method: .post, headers: headers, timeoutInterval: configuration.requestTimeout ) } catch { throw FeatureControlErrorMapper.map(error) } } public func markAllRead() async throws { var headers: [String: String] = [:] await configuration.auth.authorize(&headers) do { let _: String = try await api.request( url: configuration.baseURL + configuration.notificationsPath + "/read-all", method: .post, headers: headers, timeoutInterval: configuration.requestTimeout ) } catch { throw FeatureControlErrorMapper.map(error) } } }