;; ==========================================================================
;; adapter-html.sx — HTML string rendering adapter
;;
;; Renders evaluated SX expressions to HTML strings. Used server-side.
;;
;; Depends on:
;;   render.sx  — HTML_TAGS, VOID_ELEMENTS, BOOLEAN_ATTRS,
;;                parse-element-args, render-attrs, definition-form?
;;   eval.sx    — eval-expr, trampoline, expand-macro, process-bindings,
;;                eval-cond, env-has?, env-get, env-set!, env-merge,
;;                lambda?, component?, macro?,
;;                lambda-closure, lambda-params, lambda-body
;; ==========================================================================


(define render-to-html
  (fn (expr env)
    (case (type-of expr)
      ;; Literals — render directly
      "nil"     ""
      "string"  (escape-html expr)
      "number"  (str expr)
      "boolean" (if expr "true" "false")
      ;; List — dispatch to render-list which handles HTML tags, special forms, etc.
      "list"    (if (empty? expr) "" (render-list-to-html expr env))
      ;; Symbol — evaluate then render
      "symbol"  (render-value-to-html (trampoline (eval-expr expr env)) env)
      ;; Keyword — render as text
      "keyword" (escape-html (keyword-name expr))
      ;; Raw HTML passthrough
      "raw-html" (raw-html-content expr)
      ;; Everything else — evaluate first
      :else     (render-value-to-html (trampoline (eval-expr expr env)) env))))

(define render-value-to-html
  (fn (val env)
    (case (type-of val)
      "nil"     ""
      "string"  (escape-html val)
      "number"  (str val)
      "boolean" (if val "true" "false")
      "list"    (render-list-to-html val env)
      "raw-html" (raw-html-content val)
      "style-value" (style-value-class val)
      :else     (escape-html (str val)))))


;; --------------------------------------------------------------------------
;; Render-aware form classification
;; --------------------------------------------------------------------------

(define RENDER_HTML_FORMS
  (list "if" "when" "cond" "case" "let" "let*" "begin" "do"
        "define" "defcomp" "defmacro" "defstyle" "defkeyframes" "defhandler"
        "map" "map-indexed" "filter" "for-each"))

(define render-html-form?
  (fn (name)
    (contains? RENDER_HTML_FORMS name)))


;; --------------------------------------------------------------------------
;; render-list-to-html — dispatch on list head
;; --------------------------------------------------------------------------

(define render-list-to-html
  (fn (expr env)
    (if (empty? expr)
      ""
      (let ((head (first expr)))
        (if (not (= (type-of head) "symbol"))
          ;; Data list — render each item
          (join "" (map (fn (x) (render-value-to-html x env)) expr))
          (let ((name (symbol-name head))
                (args (rest expr)))
            (cond
              ;; Fragment
              (= name "<>")
                (join "" (map (fn (x) (render-to-html x env)) args))

              ;; Raw HTML passthrough
              (= name "raw!")
                (join "" (map (fn (x) (str (trampoline (eval-expr x env)))) args))

              ;; HTML tag
              (contains? HTML_TAGS name)
                (render-html-element name args env)

              ;; Component or macro call (~name)
              (starts-with? name "~")
                (let ((val (env-get env name)))
                  (cond
                    (component? val)
                      (render-html-component val args env)
                    (macro? val)
                      (render-to-html
                        (expand-macro val args env)
                        env)
                    :else
                      (error (str "Unknown component: " name))))

              ;; Render-aware special forms
              (render-html-form? name)
                (dispatch-html-form name expr env)

              ;; Macro expansion
              (and (env-has? env name) (macro? (env-get env name)))
                (render-to-html
                  (expand-macro (env-get env name) args env)
                  env)

              ;; Fallback — evaluate then render result
              :else
                (render-value-to-html
                  (trampoline (eval-expr expr env))
                  env))))))))


;; --------------------------------------------------------------------------
;; dispatch-html-form — render-aware special form handling for HTML output
;; --------------------------------------------------------------------------

(define dispatch-html-form
  (fn (name expr env)
    (cond
      ;; if
      (= name "if")
        (let ((cond-val (trampoline (eval-expr (nth expr 1) env))))
          (if cond-val
            (render-to-html (nth expr 2) env)
            (if (> (len expr) 3)
              (render-to-html (nth expr 3) env)
              "")))

      ;; when
      (= name "when")
        (if (not (trampoline (eval-expr (nth expr 1) env)))
          ""
          (join ""
            (map
              (fn (i) (render-to-html (nth expr i) env))
              (range 2 (len expr)))))

      ;; cond
      (= name "cond")
        (let ((branch (eval-cond (rest expr) env)))
          (if branch
            (render-to-html branch env)
            ""))

      ;; case
      (= name "case")
        (render-to-html (trampoline (eval-expr expr env)) env)

      ;; let / let*
      (or (= name "let") (= name "let*"))
        (let ((local (process-bindings (nth expr 1) env)))
          (join ""
            (map
              (fn (i) (render-to-html (nth expr i) local))
              (range 2 (len expr)))))

      ;; begin / do
      (or (= name "begin") (= name "do"))
        (join ""
          (map
            (fn (i) (render-to-html (nth expr i) env))
            (range 1 (len expr))))

      ;; Definition forms — eval for side effects
      (definition-form? name)
        (do (trampoline (eval-expr expr env)) "")

      ;; map
      (= name "map")
        (let ((f (trampoline (eval-expr (nth expr 1) env)))
              (coll (trampoline (eval-expr (nth expr 2) env))))
          (join ""
            (map
              (fn (item)
                (if (lambda? f)
                  (render-lambda-html f (list item) env)
                  (render-to-html (apply f (list item)) env)))
              coll)))

      ;; map-indexed
      (= name "map-indexed")
        (let ((f (trampoline (eval-expr (nth expr 1) env)))
              (coll (trampoline (eval-expr (nth expr 2) env))))
          (join ""
            (map-indexed
              (fn (i item)
                (if (lambda? f)
                  (render-lambda-html f (list i item) env)
                  (render-to-html (apply f (list i item)) env)))
              coll)))

      ;; filter — evaluate fully then render
      (= name "filter")
        (render-to-html (trampoline (eval-expr expr env)) env)

      ;; for-each (render variant)
      (= name "for-each")
        (let ((f (trampoline (eval-expr (nth expr 1) env)))
              (coll (trampoline (eval-expr (nth expr 2) env))))
          (join ""
            (map
              (fn (item)
                (if (lambda? f)
                  (render-lambda-html f (list item) env)
                  (render-to-html (apply f (list item)) env)))
              coll)))

      ;; Fallback
      :else
        (render-value-to-html (trampoline (eval-expr expr env)) env))))


;; --------------------------------------------------------------------------
;; render-lambda-html — render a lambda body in HTML context
;; --------------------------------------------------------------------------

(define render-lambda-html
  (fn (f args env)
    (let ((local (env-merge (lambda-closure f) env)))
      (for-each-indexed
        (fn (i p)
          (env-set! local p (nth args i)))
        (lambda-params f))
      (render-to-html (lambda-body f) local))))


;; --------------------------------------------------------------------------
;; render-html-component — expand and render a component
;; --------------------------------------------------------------------------

(define render-html-component
  (fn (comp args env)
    ;; Expand component and render body through HTML adapter.
    ;; Component body contains rendering forms (HTML tags) that only the
    ;; adapter understands, so expansion must happen here, not in eval-expr.
    (let ((kwargs (dict))
          (children (list)))
      ;; Separate keyword args from positional children
      (reduce
        (fn (state arg)
          (let ((skip (get state "skip")))
            (if skip
              (assoc state "skip" false "i" (inc (get state "i")))
              (if (and (= (type-of arg) "keyword")
                       (< (inc (get state "i")) (len args)))
                (let ((val (trampoline
                             (eval-expr (nth args (inc (get state "i"))) env))))
                  (dict-set! kwargs (keyword-name arg) val)
                  (assoc state "skip" true "i" (inc (get state "i"))))
                (do
                  (append! children arg)
                  (assoc state "i" (inc (get state "i"))))))))
        (dict "i" 0 "skip" false)
        args)
      ;; Build component env: closure + caller env + params
      (let ((local (env-merge (component-closure comp) env)))
        ;; Bind params from kwargs
        (for-each
          (fn (p)
            (env-set! local p (if (dict-has? kwargs p) (dict-get kwargs p) nil)))
          (component-params comp))
        ;; If component accepts children, pre-render them to raw HTML
        (when (component-has-children? comp)
          (env-set! local "children"
            (make-raw-html
              (join "" (map (fn (c) (render-to-html c env)) children)))))
        (render-to-html (component-body comp) local)))))


(define render-html-element
  (fn (tag args env)
    (let ((parsed (parse-element-args args env))
          (attrs  (first parsed))
          (children (nth parsed 1))
          (is-void (contains? VOID_ELEMENTS tag)))
      (str "<" tag
        (render-attrs attrs)
        (if is-void
          " />"
          (str ">"
            (join "" (map (fn (c) (render-to-html c env)) children))
            "</" tag ">"))))))


;; --------------------------------------------------------------------------
;; Platform interface — HTML adapter
;; --------------------------------------------------------------------------
;;
;; Inherited from render.sx:
;;   escape-html, escape-attr, raw-html-content, style-value?, style-value-class
;;
;; From eval.sx:
;;   eval-expr, trampoline, expand-macro, process-bindings, eval-cond
;;   env-has?, env-get, env-set!, env-merge
;;   lambda?, component?, macro?
;;   lambda-closure, lambda-params, lambda-body
;;   component-params, component-body, component-closure,
;;   component-has-children?, component-name
;;
;; Raw HTML construction:
;;   (make-raw-html s)          → wrap string as raw HTML (not double-escaped)
;;
;; Iteration:
;;   (for-each-indexed fn coll) → call fn(index, item) for each element
;;   (map-indexed fn coll)      → map fn(index, item) over each element
;; --------------------------------------------------------------------------
