import Foundation struct CardFingerprint: Codable, Identifiable, Sendable { let id: UUID let name: String let setCode: String let collectorNumber: String let hasFoilPrinting: Bool let hasSerializedPrinting: Bool? let featureData: Data var priceScanned: Double? = nil } struct CardMetadata: Identifiable, Sendable { let id: UUID let name: String let setCode: String let collectorNumber: String let hasFoilPrinting: Bool let hasSerializedPrinting: Bool var priceScanned: Double? = nil var rarity: String? = nil var colorIdentity: [String]? = nil var isSerialized: Bool = false } struct SavedCard: Codable, Identifiable, Hashable, Sendable { let id: UUID let scryfallID: String var name: String var setCode: String var collectorNumber: String let imageFileName: String var condition: String var foilType: String var currentValuation: Double? var previousValuation: Double? var dateAdded: Date var classification: String var collectionName: String var storageLocation: String var rarity: String? var colorIdentity: [String]? // Grading Fields var gradingService: String? // PSA, BGS var grade: String? // 10, 9.5 var certNumber: String? // 123456 var isCustomValuation: Bool = false var isSerialized: Bool? = false var currencyCode: String? init(from scan: CardMetadata, imageName: String, collection: String, location: String) { self.id = UUID() self.scryfallID = "\(scan.setCode)-\(scan.collectorNumber)" self.name = scan.name self.setCode = scan.setCode self.collectorNumber = scan.collectorNumber self.imageFileName = imageName self.condition = AppConfig.Defaults.defaultCondition self.foilType = AppConfig.Defaults.defaultFoil self.currentValuation = scan.priceScanned self.previousValuation = scan.priceScanned self.dateAdded = Date() self.classification = "Unknown" self.collectionName = collection self.storageLocation = location self.rarity = scan.rarity self.colorIdentity = scan.colorIdentity self.isSerialized = scan.isSerialized } /// Full memberwise init used by SavedCardModel (SwiftData) ↔ SavedCard conversion. init(id: UUID, scryfallID: String, name: String, setCode: String, collectorNumber: String, imageFileName: String, condition: String, foilType: String, currentValuation: Double?, previousValuation: Double?, dateAdded: Date, classification: String, collectionName: String, storageLocation: String, rarity: String?, colorIdentity: [String]?, gradingService: String?, grade: String?, certNumber: String?, isCustomValuation: Bool, isSerialized: Bool?, currencyCode: String?) { self.id = id self.scryfallID = scryfallID self.name = name self.setCode = setCode self.collectorNumber = collectorNumber self.imageFileName = imageFileName self.condition = condition self.foilType = foilType self.currentValuation = currentValuation self.previousValuation = previousValuation self.dateAdded = dateAdded self.classification = classification self.collectionName = collectionName self.storageLocation = storageLocation self.rarity = rarity self.colorIdentity = colorIdentity self.gradingService = gradingService self.grade = grade self.certNumber = certNumber self.isCustomValuation = isCustomValuation self.isSerialized = isSerialized self.currencyCode = currencyCode } } enum MatchResult { case exact(CardMetadata) case ambiguous(name: String, candidates: [CardMetadata]) case unknown }