;; content-on-sx — external CMS sync via an injected adapter.
;;
;; Sync is a peripheral, not a feature. The core defines a SHAPE — an adapter is
;; a dict {:import (fn external doc-id -> doc) :export (fn doc -> external)} — and
;; delegates to it. The core knows nothing about Ghost's data model; all
;; translation lives in the adapter. Swap the adapter and the core is unchanged;
;; if Ghost goes away, nothing here does.
;;
;; Requires (loaded by harness): block.sx, doc.sx.

;; ── generic boundary: pure delegation ──
(define
  content/import
  (fn (adapter external doc-id) ((get adapter :import) external doc-id)))

(define content/export (fn (adapter doc) ((get adapter :export) doc)))

;; round-trip a document through an adapter (export then import).
(define
  content/round-trip
  (fn
    (adapter doc)
    (content/import adapter (content/export adapter doc) (doc-id doc))))

;; ── a Ghost-flavoured adapter (the peripheral). Ghost knowledge is confined
;; here: a post is {:title :sections (list section)}; a section is a tagged dict
;; {:kind ...} that this adapter maps to/from content blocks. ──
(define
  ghost-section->block
  (fn
    (sec)
    (let
      ((kind (get sec :kind)) (id (get sec :id)))
      (cond
        ((= kind "heading")
          (mk-heading id (get sec :level) (get sec :text)))
        ((= kind "paragraph") (mk-text id (get sec :text)))
        ((= kind "image") (mk-image id (get sec :src) (get sec :alt)))
        ((= kind "code") (mk-code id (get sec :language) (get sec :text)))
        ((= kind "quote") (mk-quote id (get sec :cite) (get sec :text)))
        ((= kind "hr") (mk-divider id))
        ((= kind "list") (mk-list id (get sec :ordered) (get sec :items)))
        ((= kind "embed") (mk-embed id (get sec :url) (get sec :provider)))
        (else (mk-text id (get sec :text)))))))

(define
  block->ghost-section
  (fn
    (b)
    (let
      ((t (blk-type b)) (id (blk-id b)))
      (cond
        ((= t "heading") {:id id :text (str (blk-send b "text")) :kind "heading" :level (blk-send b "level")})
        ((= t "text") {:id id :text (str (blk-send b "text")) :kind "paragraph"})
        ((= t "image") {:id id :src (str (blk-send b "src")) :alt (str (blk-send b "alt")) :kind "image"})
        ((= t "code") {:id id :text (str (blk-send b "text")) :kind "code" :language (str (blk-send b "language"))})
        ((= t "quote") {:cite (str (blk-send b "cite")) :id id :text (str (blk-send b "text")) :kind "quote"})
        ((= t "divider") {:id id :kind "hr"})
        ((= t "list") {:items (blk-send b "items") :id id :kind "list" :ordered (blk-send b "ordered")})
        ((= t "embed") {:id id :provider (str (blk-send b "provider")) :kind "embed" :url (str (blk-send b "url"))})
        (else {:id id :text "" :kind "paragraph"})))))

(define
  ghost-import
  (fn
    (post doc-id)
    (st-iv-set!
      (doc-new doc-id (map ghost-section->block (get post :sections)))
      "title"
      (get post :title))))

(define ghost-export (fn (doc) {:sections (map block->ghost-section (doc-blocks doc)) :title (st-send doc "title" (list))}))

(define ghost-adapter {:export ghost-export :import ghost-import})
