;; Step 15: Bytecode + continuation serialization
;; Round-trip compiled code and suspended CEK state through strings.

;; ── Bytecode serialization ────────────────────────────────────────

(defsuite
  "bytecode-serialize-basic"
  (deftest
    "compile and serialize round-trips"
    (let
      ((code (compile "(+ 1 2)")))
      (assert (not (nil? code)))
      (let
        ((serialized (bytecode-serialize code)))
        (assert (string? serialized))
        (assert (> (string-length serialized) 0)))))
  (deftest
    "serialized format has sxbc header"
    (let
      ((code (compile "(+ 1 2)")))
      (let
        ((serialized (bytecode-serialize code)))
        (assert (starts-with? serialized "(sxbc 2")))))
  (deftest
    "deserialized code has same structure"
    (let
      ((code (compile "(+ 1 2)")))
      (let
        ((serialized (bytecode-serialize code))
          (restored (bytecode-deserialize serialized)))
        (assert (not (nil? restored)))
        (assert (dict? restored)))))
  (deftest
    "deserialized bytecode array matches"
    (let
      ((code (compile "(+ 1 2)")))
      (let
        ((serialized (bytecode-serialize code))
          (restored (bytecode-deserialize serialized)))
        (assert= (get restored "bytecode") (get code "bytecode")))))
  (deftest
    "deserialized constants match"
    (let
      ((code (compile "(str \"hello\" \" \" \"world\")")))
      (let
        ((serialized (bytecode-serialize code))
          (restored (bytecode-deserialize serialized)))
        (assert= (get restored "constants") (get code "constants"))))))

(defsuite
  "bytecode-serialize-types"
  (deftest
    "number bytecodes round-trip"
    (let
      ((code (compile "(+ 42 3)")))
      (let
        ((restored (bytecode-deserialize (bytecode-serialize code))))
        (assert= (get restored "bytecode") (get code "bytecode")))))
  (deftest
    "multiple expressions round-trip"
    (let
      ((code (compile "(define x 1)")))
      (let
        ((restored (bytecode-deserialize (bytecode-serialize code))))
        (assert= (get restored "bytecode") (get code "bytecode"))
        (assert= (get restored "constants") (get code "constants")))))
  (deftest
    "dict expression round-trips"
    (let
      ((code (compile "(dict :a 1 :b 2)")))
      (let
        ((restored (bytecode-deserialize (bytecode-serialize code))))
        (assert= (get restored "constants") (get code "constants")))))
  (deftest
    "lambda expression round-trips"
    (let
      ((code (compile "(fn (x) (+ x 1))")))
      (let
        ((restored (bytecode-deserialize (bytecode-serialize code))))
        (assert= (get restored "bytecode") (get code "bytecode")))))
  (deftest
    "invalid format raises error"
    (let
      ((raised false))
      (guard
        (e (true (set! raised true)))
        (bytecode-deserialize "not valid"))
      (assert raised "should raise for invalid format"))))

;; ── CEK state serialization ───────────────────────────────────────

(defsuite
  "cek-serialize-basic"
  (deftest
    "suspended state serializes"
    (let
      ((state (make-cek-suspended {:op "test"} (dict) (list))))
      (let
        ((serialized (cek-serialize state)))
        (assert (string? serialized))
        (assert (starts-with? serialized "(cek-state 1")))))
  (deftest
    "deserialized state preserves phase"
    (let
      ((state (make-cek-suspended {:op "test"} (dict) (list))))
      (let
        ((restored (cek-deserialize (cek-serialize state))))
        (assert (cek-suspended? restored)))))
  (deftest
    "deserialized state preserves request"
    (let
      ((state (make-cek-suspended {:url "/api" :op "fetch"} (dict) (list))))
      (let
        ((restored (cek-deserialize (cek-serialize state))))
        (let
          ((req (cek-io-request restored)))
          (assert= (get req :op) "fetch")
          (assert= (get req :url) "/api"))))))

(defsuite
  "cek-serialize-values"
  (deftest
    "request with nested data round-trips"
    (let
      ((req {:params {:ids (list 1 2 3) :name "test"} :op "query"}) (state (make-cek-suspended req (dict) (list))))
      (let
        ((restored (cek-deserialize (cek-serialize state))))
        (let
          ((rreq (cek-io-request restored)))
          (assert= (get rreq :op) "query")
          (assert= (get (get rreq :params) :name) "test")
          (assert= (get (get rreq :params) :ids) (list 1 2 3))))))
  (deftest
    "string with special chars round-trips"
    (let
      ((req {:op "test" :data "line1\nline2\ttab"}) (state (make-cek-suspended req (dict) (list))))
      (let
        ((restored (cek-deserialize (cek-serialize state))))
        (assert= (get (cek-io-request restored) :data) "line1\nline2\ttab"))))
  (deftest
    "invalid cek format raises error"
    (let
      ((raised false))
      (guard (e (true (set! raised true))) (cek-deserialize "garbage"))
      (assert raised "should raise for invalid format"))))
