import Foundation
import Quartz
import UniformTypeIdentifiers

/// **Space in Finder, on a `.kanban` package** — the whole of this extension's job.
///
/// A board is a folder, and a folder previews as a folder: an icon and nothing else. This gives it a
/// document's preview instead — its name, its tint, its lanes in order, each lane's card count and
/// its first few card titles — built from `BoardOutline.read`, the capped read-only walk that lives
/// beside the storage format it reads (Kanban/Storage/BoardOutline.swift).
///
/// ### Data-based, not view-based
///
/// `QLPreviewProvider` is the data-based half of the Quick Look preview API: the extension answers
/// with *content* (`QLPreviewReply`) and Quick Look owns the window. The view-based half
/// (`QLPreviewingController` on an `NSViewController`) would put a SwiftUI hierarchy on screen, and
/// this preview has no use for one — it is static by design (v1: no interactivity, no attachment or
/// image loading), so a view controller would be a lifecycle to keep honest in exchange for nothing.
///
/// The reply is **HTML**, which is the one supported data type that reflows: a Quick Look panel is
/// resized by the user and a board summary is a wrapping row of columns, so a fixed `contentSize`
/// drawing block or a rendered image would be the wrong shape the moment the panel is not the size
/// it was baked at. HTML also gets vector text, selectable text, its own scrolling, and light/dark
/// through `prefers-color-scheme` — all of which a `CGContext` reply would have to reinvent.
///
/// ### Where the work happens
///
/// Everything is inside the reply's data-creation block, which is where Apple's own documentation
/// puts it ("Heavy lifting should be done inside of the dataCreationBlock instead of when creating
/// the QLPreviewReply"): `providePreview` returns immediately with a size hint, Quick Look draws its
/// loading state at the right size, and the walk runs while it does.
///
/// ### Reading the board
///
/// The extension is sandboxed with read access to the URL it was handed and nothing else, which is
/// exactly what the walk needs — it opens `index.md` files under `request.fileURL` and never looks
/// outside the package. `BoardOutline.read` does not throw: a folder that is not a board, a board
/// with a broken `index.md`, a permissions race mid-walk all yield an outline of whatever *did* read,
/// so this method has no error path of its own and Quick Look never sees a failed preview where it
/// could have shown a name.
final class BoardPreviewProvider: QLPreviewProvider, QLPreviewingController {

    func providePreview(for request: QLFilePreviewRequest) async throws -> QLPreviewReply {
        let boardRoot = request.fileURL
        return QLPreviewReply(
            dataOfContentType: .html,
            contentSize: BoardPreviewPage.contentSizeHint
        ) { reply in
            let outline = BoardOutline.read(boardRoot: boardRoot)
            reply.stringEncoding = .utf8
            // The panel's own title bar. Left empty, Quick Look uses the file name — which is the
            // folder name, and therefore *wrong* for every board whose `title:` differs from it.
            reply.title = outline.title
            reply.attachments = BoardPreviewPage.attachments(for: outline)
            return BoardPreviewPage.html(for: outline)
        }
    }
}
