;; lib/mod/lifecycle.sx — report lifecycle state machine (pure SX over the engine).
;;
;; Lifecycle state is deliberately separate from policy: the Prolog rules answer
;; "what action?", this module answers "where in the process is this report?".
;;
;;   :open ──triage──▶ :triaged ──resolve/review──▶ :decided ──appeal──▶ :appealed
;;                                                      │                    │
;;                                                      └────finalize───▶ :final ◀┘
;;
;; A case is an immutable value {:report :state :decision :tier :error :history}.
;; Every transition returns a NEW case; illegal transitions return the case
;; unchanged with :error set. Tiers: triage runs the engine (auto-tier); a
;; terminal action (hide/remove/keep) resolves immediately, an :escalate action
;; flags the case for human review (human-tier) before it can be resolved.

(define mod/case* (fn (report state decision tier err history) {:history history :state state :report report :error err :tier tier :decision decision}))

(define
  mod/mk-case
  (fn (report) (mod/case* report "open" nil nil nil (list))))

(define mod/case-report (fn (c) (get c :report)))
(define mod/case-state (fn (c) (get c :state)))
(define mod/case-decision (fn (c) (get c :decision)))
(define mod/case-tier (fn (c) (get c :tier)))
(define mod/case-error (fn (c) (get c :error)))
(define mod/case-history (fn (c) (get c :history)))

;; ── transition table ──

(define mod/lc-transitions {:final (list) :appealed (list "final") :decided (list "appealed" "final") :open (list "triaged") :triaged (list "decided")})

(define mod/member? (fn (x lst) (mod/any? (fn (y) (= y x)) lst)))

(define
  mod/lc-can-transition?
  (fn
    (from to)
    (let
      ((outs (get mod/lc-transitions from)))
      (if (nil? outs) false (mod/member? to outs)))))

;; ── core transition: validate, record history, or flag :error ──

(define
  mod/case-goto
  (fn
    (c to note report decision tier)
    (let
      ((from (mod/case-state c)))
      (if
        (mod/lc-can-transition? from to)
        (mod/case*
          report
          to
          decision
          tier
          nil
          (append (mod/case-history c) (list {:note note :to to :from from})))
        (mod/case*
          (mod/case-report c)
          from
          (mod/case-decision c)
          (mod/case-tier c)
          (str "illegal transition: " from " -> " to)
          (mod/case-history c))))))

(define
  mod/case-error-set
  (fn
    (c msg)
    (mod/case*
      (mod/case-report c)
      (mod/case-state c)
      (mod/case-decision c)
      (mod/case-tier c)
      msg
      (mod/case-history c))))

;; ── lifecycle operations ──

;; :open → :triaged — run the auto-tier first pass.
(define
  mod/case-triage
  (fn
    (c reports rules)
    (let
      ((d (mod/decide-report (mod/case-report c) reports rules)))
      (let
        ((tier (if (= (get d :action) "escalate") "human" "auto")))
        (mod/case-goto
          c
          "triaged"
          "auto-tier first pass"
          (mod/case-report c)
          d
          tier)))))

;; :triaged → :decided — auto-tier resolves; human-tier is blocked until review.
(define
  mod/case-resolve
  (fn
    (c)
    (if
      (= (mod/case-tier c) "human")
      (mod/case-error-set c "awaiting human review (escalated)")
      (mod/case-goto
        c
        "decided"
        "auto-tier resolved"
        (mod/case-report c)
        (mod/case-decision c)
        (mod/case-tier c)))))

;; :triaged → :decided — human review: attach evidence, re-decide, resolve.
(define
  mod/case-review
  (fn
    (c kind val reports rules)
    (let
      ((nr (mod/attach-evidence (mod/case-report c) (mod/mk-evidence kind val))))
      (let
        ((d (mod/decide-report nr reports rules)))
        (mod/case-goto c "decided" (str "human review: " kind) nr d "human")))))

;; :decided → :appealed — appeal: attach evidence, re-decide (may override).
(define
  mod/case-appeal
  (fn
    (c kind val reports rules)
    (let
      ((nr (mod/attach-evidence (mod/case-report c) (mod/mk-evidence kind val))))
      (let
        ((d (mod/decide-report nr reports rules)))
        (mod/case-goto
          c
          "appealed"
          (str "appeal: " kind)
          nr
          d
          (mod/case-tier c))))))

;; :decided | :appealed → :final
(define
  mod/case-finalize
  (fn
    (c)
    (mod/case-goto
      c
      "final"
      "finalized"
      (mod/case-report c)
      (mod/case-decision c)
      (mod/case-tier c))))

(define
  mod/case-action
  (fn
    (c)
    (let ((d (mod/case-decision c))) (if (nil? d) nil (get d :action)))))
