import AppKit

/// The `icon` field's rendering rule: **a name that resolves is drawn, anything else falls back to
/// the level's default** (03-board-ui.md § Styling ▸ Capabilities, "`icon`: SF Symbol per item with
/// per-level defaults").
///
/// ### Lenient, never an error
///
/// `icon` is a lenient field (01-storage-format.md § Frontmatter): a typo in a hand-written name is
/// not a load failure, not a warning, and not an empty box — it renders as the default and the
/// author's bytes are left exactly as written until they change them. That mirrors the read side's
/// treatment of `width`, and it is what makes "any other SF Symbol name works written by hand"
/// (03 § Controls) safe to promise: the app's curated grid is a convenience, the file is the escape
/// hatch, and a name the running OS does not happen to have simply degrades.
///
/// `NSImage(systemSymbolName:)` is the only honest test — the symbol set is the *OS's*, and it grows
/// between releases, so a hard-coded allow-list would be wrong the day after it was written.
enum ItemSymbol {

    /// The per-level defaults, verbatim from 03-board-ui.md § Styling ▸ Capabilities.
    static let board = "rectangle.split.3x1"
    static let lane = "square.stack"
    static let card = "doc.text"

    /// `field`'s symbol name if it names a symbol this system can draw, `fallback` otherwise.
    ///
    /// Handles all three `FieldValue` shapes the same way, which is the point: a missing key, a
    /// malformed one (a sequence where a scalar belongs), and a valid-but-unknown name are one
    /// case to the renderer — *there is no symbol to draw, so draw the default*.
    static func name(_ field: FieldValue<String>, fallback: String) -> String {
        guard let name = field.value, exists(name) else { return fallback }
        return name
    }

    /// The default for a level — the style editor's symbol grid needs the three defaults as a
    /// function rather than as three constants, because its leading well is "the level's default
    /// symbol" and the editor is one component serving all three (03-board-ui.md § Styling ▸
    /// Controls).
    static func `default`(for level: StyleLevel) -> String {
        switch level {
        case .board: board
        case .lane: lane
        case .card: card
        }
    }

    /// Whether the running system can draw `name` as an SF Symbol.
    ///
    /// Uncached deliberately. The lookup is a bundle-backed symbol resolution that AppKit itself
    /// caches, it runs once per lane per render pass, and a cache here would be one more piece of
    /// main-actor state to keep honest across the OS's own symbol availability. If a profile ever
    /// says otherwise, this one function is where the memo goes.
    static func exists(_ name: String) -> Bool {
        !name.isEmpty && NSImage(systemSymbolName: name, accessibilityDescription: nil) != nil
    }
}
