import Foundation public enum RDEPUBNoteDetector { private static let noteTokens = [ "noteref", "footnote", "endnote", "rearnote", "fnref", "fn-", "fn_", "note", "annotation" ] public static func makeReferenceIfLikelyNote( sourceHref: String, sourceCFI: String?, targetHref: String, targetFragment: String?, targetCFI: String?, targetElementHTML: String? ) -> RDEPUBNoteReference? { let haystack = [ targetHref, targetFragment ?? "", targetElementHTML ?? "" ].joined(separator: " ").lowercased() guard noteTokens.contains(where: { haystack.contains($0) }) || hasEPUBType("footnote", in: targetElementHTML) || hasEPUBType("endnote", in: targetElementHTML) || hasEPUBType("rearnote", in: targetElementHTML) else { return nil } return RDEPUBNoteReference( sourceHref: sourceHref, sourceCFI: sourceCFI, targetHref: targetHref, targetFragment: targetFragment, targetCFI: targetCFI, label: targetFragment, kind: kind(from: haystack) ) } public static func kind(from text: String) -> RDEPUBNoteKind { let lowered = text.lowercased() if lowered.contains("endnote") { return .endnote } if lowered.contains("rearnote") { return .rearnote } if lowered.contains("footnote") || lowered.contains("fn") { return .footnote } return .unknown } private static func hasEPUBType(_ type: String, in html: String?) -> Bool { guard let html else { return false } let pattern = #"epub:type\s*=\s*['"][^'"]*\b"# + NSRegularExpression.escapedPattern(for: type) + #"\b[^'"]*['"]"# return html.range(of: pattern, options: [.regularExpression, .caseInsensitive]) != nil } }