import Vision import CoreML import UIKit struct DamageObservation: Identifiable, Sendable { let id = UUID() let type: String let rect: CGRect let confidence: Float } // MARK: - CONDITION ENGINE class ConditionEngine { enum Severity: Int { case minor = 1; case critical = 10 } static var model: VNCoreMLModel? = { guard AppConfig.enableConditionGrading else { return nil } return ModelManager.shared.getModel(name: "IYmtgConditionClassifier") }() static func getSeverity(for type: String) -> Severity { return (type == "Inking" || type == "Rips" || type == "WaterDamage") ? .critical : .minor } static func detectDamage(image: CGImage, orientation: CGImagePropertyOrientation = .up) -> [DamageObservation] { guard let model = model else { return [] } let request = VNCoreMLRequest(model: model) request.imageCropAndScaleOption = .scaleFill let handler = VNImageRequestHandler(cgImage: image, orientation: orientation, options: [:]) do { try handler.perform([request]) guard let results = request.results as? [VNRecognizedObjectObservation] else { return [] } return results.filter { $0.confidence > 0.7 }.map { obs in DamageObservation(type: obs.labels.first?.identifier ?? "Unknown", rect: obs.boundingBox, confidence: obs.confidence) } } catch { return [] } } static func overallGrade(damages: [DamageObservation]) -> String { if model == nil && damages.isEmpty { return "Ungraded" } var score = 0 for d in damages { if getSeverity(for: d.type) == .critical { return "Damaged" } score += 1 } if score == 0 { return "Near Mint (NM)" } if score <= 2 { return "Excellent (EX)" } return "Played (PL)" } }