(defsuite
  "parse-time"
  (deftest "seconds to ms" (assert-equal 2000 (parse-time "2s")))
  (deftest "milliseconds" (assert-equal 500 (parse-time "500ms")))
  (deftest "nil returns 0" (assert-equal 0 (parse-time nil)))
  (deftest "plain number string" (assert-equal 100 (parse-time "100")))
  (deftest "one second" (assert-equal 1000 (parse-time "1s")))
  (deftest "large seconds" (assert-equal 30000 (parse-time "30s"))))

(defsuite
  "parse-trigger-spec"
  (deftest "nil returns nil" (assert-nil (parse-trigger-spec nil)))
  (deftest
    "single event"
    (let
      ((triggers (parse-trigger-spec "click")))
      (assert-equal 1 (len triggers))
      (assert-equal "click" (get (first triggers) "event"))))
  (deftest
    "event with once modifier"
    (let
      ((triggers (parse-trigger-spec "click once")))
      (assert-equal 1 (len triggers))
      (assert-equal "click" (get (first triggers) "event"))
      (assert-true (get (get (first triggers) "modifiers") "once"))))
  (deftest
    "event with delay modifier"
    (let
      ((triggers (parse-trigger-spec "click delay:500ms")))
      (assert-equal 1 (len triggers))
      (assert-equal 500 (get (get (first triggers) "modifiers") "delay"))))
  (deftest
    "multiple triggers comma-separated"
    (let
      ((triggers (parse-trigger-spec "click,change")))
      (assert-equal 2 (len triggers))
      (assert-equal "click" (get (first triggers) "event"))
      (assert-equal "change" (get (nth triggers 1) "event"))))
  (deftest
    "polling trigger"
    (let
      ((triggers (parse-trigger-spec "every 3s")))
      (assert-equal 1 (len triggers))
      (assert-equal "every" (get (first triggers) "event"))
      (assert-equal
        3000
        (get (get (first triggers) "modifiers") "interval"))))
  (deftest
    "event with from modifier"
    (let
      ((triggers (parse-trigger-spec "click from:body")))
      (assert-equal "body" (get (get (first triggers) "modifiers") "from"))))
  (deftest
    "event with changed modifier"
    (let
      ((triggers (parse-trigger-spec "keyup changed")))
      (assert-equal "keyup" (get (first triggers) "event"))
      (assert-true (get (get (first triggers) "modifiers") "changed")))))

(defsuite
  "default-trigger"
  (deftest
    "form submits"
    (let
      ((triggers (default-trigger "form")))
      (assert-equal "submit" (get (first triggers) "event"))))
  (deftest
    "input changes"
    (let
      ((triggers (default-trigger "input")))
      (assert-equal "change" (get (first triggers) "event"))))
  (deftest
    "select changes"
    (let
      ((triggers (default-trigger "select")))
      (assert-equal "change" (get (first triggers) "event"))))
  (deftest
    "textarea changes"
    (let
      ((triggers (default-trigger "textarea")))
      (assert-equal "change" (get (first triggers) "event"))))
  (deftest
    "div clicks"
    (let
      ((triggers (default-trigger "div")))
      (assert-equal "click" (get (first triggers) "event"))))
  (deftest
    "button clicks"
    (let
      ((triggers (default-trigger "button")))
      (assert-equal "click" (get (first triggers) "event")))))

(defsuite
  "parse-swap-spec"
  (deftest
    "default swap"
    (let
      ((spec (parse-swap-spec nil false)))
      (assert-equal "outerHTML" (get spec "style"))
      (assert-false (get spec "transition"))))
  (deftest
    "innerHTML"
    (let
      ((spec (parse-swap-spec "innerHTML" false)))
      (assert-equal "innerHTML" (get spec "style"))))
  (deftest
    "with transition true"
    (let
      ((spec (parse-swap-spec "innerHTML transition:true" false)))
      (assert-equal "innerHTML" (get spec "style"))
      (assert-true (get spec "transition"))))
  (deftest
    "transition false overrides global"
    (let
      ((spec (parse-swap-spec "outerHTML transition:false" true)))
      (assert-equal "outerHTML" (get spec "style"))
      (assert-false (get spec "transition"))))
  (deftest
    "global transition when not overridden"
    (let
      ((spec (parse-swap-spec "innerHTML" true)))
      (assert-equal "innerHTML" (get spec "style"))
      (assert-true (get spec "transition")))))

(defsuite
  "parse-retry-spec"
  (deftest "nil returns nil" (assert-nil (parse-retry-spec nil)))
  (deftest
    "exponential backoff"
    (let
      ((spec (parse-retry-spec "exponential:1000:30000")))
      (assert-equal "exponential" (get spec "strategy"))
      (assert-equal 1000 (get spec "start-ms"))
      (assert-equal 30000 (get spec "cap-ms"))))
  (deftest
    "linear strategy"
    (let
      ((spec (parse-retry-spec "linear:2000:60000")))
      (assert-equal "linear" (get spec "strategy"))
      (assert-equal 2000 (get spec "start-ms"))
      (assert-equal 60000 (get spec "cap-ms")))))

(defsuite
  "next-retry-ms"
  (deftest "doubles current" (assert-equal 2000 (next-retry-ms 1000 30000)))
  (deftest
    "caps at maximum"
    (assert-equal 30000 (next-retry-ms 20000 30000)))
  (deftest "exact cap" (assert-equal 30000 (next-retry-ms 15000 30000)))
  (deftest "small initial" (assert-equal 200 (next-retry-ms 100 30000))))

(defsuite
  "filter-params"
  (deftest
    "nil passes all through"
    (let
      ((params (list (list "a" "1") (list "b" "2"))))
      (assert-equal 2 (len (filter-params nil params)))))
  (deftest
    "none returns empty"
    (let
      ((params (list (list "a" "1") (list "b" "2"))))
      (assert-equal 0 (len (filter-params "none" params)))))
  (deftest
    "star passes all"
    (let
      ((params (list (list "a" "1") (list "b" "2"))))
      (assert-equal 2 (len (filter-params "*" params)))))
  (deftest
    "whitelist"
    (let
      ((params (list (list "name" "Jo") (list "age" "30") (list "secret" "x"))))
      (let
        ((filtered (filter-params "name,age" params)))
        (assert-equal 2 (len filtered)))))
  (deftest
    "blacklist with not"
    (let
      ((params (list (list "name" "Jo") (list "csrf" "tok") (list "age" "30"))))
      (let
        ((filtered (filter-params "not csrf" params)))
        (assert-equal 2 (len filtered))))))
