// // DocuframeService.swift // GasTankAR // // Created by Dennis Nemec on 28.08.26. // import Foundation /// Foto-Übergabeobjekt für DOCUframe-Vorgänge – bewusst unabhängig von der /// Persistenzschicht (SwiftData), damit die Domäne keine Speicher-Details kennt. struct DocuframePhoto: Sendable { let imageData: Data let creationDate: Date let latitude: Double? let longitude: Double? } /// Fehler bei DOCUframe-Vorgängen. enum DocuframeError: LocalizedError, Equatable { case noPhotosSelected /// Der Server hat mit einem 4xx-Status geantwortet (fehlerhafte Anfrage). case clientError(statusCode: Int) /// Der Server hat mit einem 5xx-Status geantwortet (Serverproblem). case serverError(statusCode: Int) /// Netzwerk-/Transportfehler (z. B. keine Verbindung). case transport(String) var errorDescription: String? { switch self { case .noPhotosSelected: "Es wurden keine Fotos ausgewählt." case .clientError(let statusCode): "Die Anfrage wurde von DOCUframe abgelehnt (Fehler \(statusCode)). Bitte prüfe die Daten und versuche es erneut." case .serverError(let statusCode): "DOCUframe ist derzeit nicht erreichbar (Serverfehler \(statusCode)). Bitte versuche es später erneut." case .transport(let message): message } } /// Bildet einen HTTP-Statuscode auf den passenden Fehler ab – `nil` bei Erfolg (2xx/3xx). static func from(statusCode: Int) -> DocuframeError? { switch statusCode { case 400..<500: .clientError(statusCode: statusCode) case 500..<600: .serverError(statusCode: statusCode) default: nil } } } extension HTTPURLResponse { /// Wirft einen ``DocuframeError``, wenn der Statuscode einen 4xx-/5xx-Fehler darstellt. func validateDocuframeStatus() throws { if let error = DocuframeError.from(statusCode: statusCode) { throw error } } } /// Abstraktion des DOCUframe-Zugriffs (Upload & Mailversand). /// /// Clean-Architecture-Grenze: Die Präsentationsschicht hängt nur von diesem Protokoll ab, /// nicht von der konkreten DOCUframe-Anbindung. Konkrete Implementierungen (Stub, echte /// Anbindung, Mock für Tests) liegen in der Infrastruktur- bzw. Testschicht. protocol DocuframeService: Sendable { /// Lädt die übergebenen Fotos zum Projekt (Lead) in DOCUframe hoch. func uploadPhotos(_ photos: [DocuframePhoto], projectLeadID: String) async throws /// Versendet die übergebenen Fotos als E-Mail über DOCUframe. func sendEmail(_ photos: [DocuframePhoto], projectLeadID: String) async throws }