(defsuite
  "sx-swap:innerHTML"
  (deftest
    "replaces children of target"
    (let
      ((result (sx-swap "(div :id \"t\" (p \"old\"))" "innerHTML" "t" "(p \"new\")")))
      (assert-equal result "(div :id \"t\" (p \"new\"))")))
  (deftest
    "replaces multiple children"
    (let
      ((result (sx-swap "(div :id \"t\" (p \"a\") (p \"b\"))" "innerHTML" "t" "(span \"x\")")))
      (assert-equal result "(div :id \"t\" (span \"x\"))")))
  (deftest
    "handles nested target"
    (let
      ((result (sx-swap "(main (div :id \"t\" (p \"old\")))" "innerHTML" "t" "(p \"new\")")))
      (assert-equal result "(main (div :id \"t\" (p \"new\")))")))
  (deftest
    "preserves attrs"
    (let
      ((result (sx-swap "(div :id \"t\" :class \"box\" (p \"old\"))" "innerHTML" "t" "(p \"new\")")))
      (assert-equal result "(div :id \"t\" :class \"box\" (p \"new\"))"))))

(defsuite
  "sx-swap:outerHTML"
  (deftest
    "replaces entire element"
    (let
      ((result (sx-swap "(main (div :id \"t\" (p \"old\")) (footer \"f\"))" "outerHTML" "t" "(section \"new\")")))
      (assert-equal result "(main (section \"new\") (footer \"f\"))")))
  (deftest
    "replaces at root"
    (let
      ((result (sx-swap "(div :id \"t\" (p \"old\"))" "outerHTML" "t" "(span \"new\")")))
      (assert-equal result "(span \"new\")"))))

(defsuite
  "sx-swap:beforeend"
  (deftest
    "appends to children"
    (let
      ((result (sx-swap "(ul :id \"t\" (li \"a\"))" "beforeend" "t" "(li \"b\")")))
      (assert-equal result "(ul :id \"t\" (li \"a\") (li \"b\"))")))
  (deftest
    "appends to empty element"
    (let
      ((result (sx-swap "(div :id \"t\")" "beforeend" "t" "(p \"new\")")))
      (assert-equal result "(div :id \"t\" (p \"new\"))"))))

(defsuite
  "sx-swap:afterbegin"
  (deftest
    "prepends to children"
    (let
      ((result (sx-swap "(ul :id \"t\" (li \"b\"))" "afterbegin" "t" "(li \"a\")")))
      (assert-equal result "(ul :id \"t\" (li \"a\") (li \"b\"))"))))

(defsuite
  "sx-swap:beforebegin"
  (deftest
    "inserts before element"
    (let
      ((result (sx-swap "(div (p :id \"t\" \"x\"))" "beforebegin" "t" "(hr)")))
      (assert-equal result "(div (hr)(p :id \"t\" \"x\"))"))))

(defsuite
  "sx-swap:afterend"
  (deftest
    "inserts after element"
    (let
      ((result (sx-swap "(div (p :id \"t\" \"x\") (span \"y\"))" "afterend" "t" "(hr)")))
      (assert-equal result "(div (p :id \"t\" \"x\")(hr) (span \"y\"))"))))

(defsuite
  "sx-swap:delete"
  (deftest
    "removes element"
    (let
      ((result (sx-swap "(div (p :id \"t\" \"bye\") (p \"stay\"))" "delete" "t" "")))
      (assert-true (contains? result "stay"))
      (assert-false (contains? result "bye")))))

(defsuite
  "sx-swap:none"
  (deftest
    "returns unchanged"
    (let
      ((page "(div :id \"t\" (p \"x\"))"))
      (assert-equal (sx-swap page "none" "t" "(p \"y\")") page))))

(defsuite
  "sx-swap:missing-target"
  (deftest
    "returns unchanged when id not found"
    (let
      ((page "(div :id \"other\" (p \"x\"))"))
      (assert-equal (sx-swap page "innerHTML" "missing" "(p \"y\")") page))))

(defsuite
  "sx-swap:oob"
  (deftest
    "finds oob elements"
    (let
      ((src "(<> (p \"main\") (div :id \"oob-t\" :sx-swap-oob \"innerHTML\" (p \"oob\")))"))
      (let
        ((oobs (find-oob-elements src)))
        (assert-equal (len oobs) 1)
        (assert-equal (get (first oobs) "id") "oob-t")
        (assert-equal (get (first oobs) "mode") "innerHTML"))))
  (deftest
    "strips oob from response"
    (let
      ((src "(<> (p \"main\") (div :id \"oob-t\" :sx-swap-oob \"innerHTML\" (p \"oob\")))"))
      (let
        ((oobs (find-oob-elements src)))
        (let
          ((main (strip-oob src oobs)))
          (assert-true (contains? main "main"))
          (assert-false (contains? main "oob-t"))))))
  (deftest
    "full pipeline applies primary + oob"
    (let
      ((page "(div (div :id \"a\" (p \"A old\")) (div :id \"b\" (p \"B old\")))")
        (response
          "(<> (p \"A new\") (div :id \"b\" :sx-swap-oob \"innerHTML\" (p \"B new\")))"))
      (let
        ((result (apply-response page response "innerHTML" "a")))
        (assert-true (contains? result "A new"))
        (assert-true (contains? result "B new"))
        (assert-false (contains? result "A old"))
        (assert-false (contains? result "B old"))))))
