-- t-paliad-183 / Fristen Phase 3 Slice 2 Step B-3 — backfill
-- paliad.deadline_rules.condition_expr from the legacy
-- condition_flag text[] column per DESIGN §2.4 long form (NOT the
-- short {"and":[...]} form sketched in head's msg 1746 — head's
-- clarification msg 1750 rules in favour of the design doc).
--
-- Mapping (design §2.4):
--
--   condition_flag IS NULL OR array_length(_, 1) = 0
--       → condition_expr stays NULL (unconditional, every rule renders)
--
--   array_length = 1, e.g. ['with_ccr']
--       → condition_expr = jsonb '{"flag": "with_ccr"}'
--         (single flag unwrapped — saves a layer of nesting that
--          parses as the same boolean expression)
--
--   array_length >= 2, e.g. ['with_ccr', 'with_amend']
--       → condition_expr = jsonb '{"op":"and","args":[
--                                    {"flag":"with_ccr"},
--                                    {"flag":"with_amend"}
--                                ]}'
--         (long form — same shape the rule editor will emit for OR /
--          NOT in future rules so the calculator's parser is uniform)
--
-- Why long form on >=2: the calculator (Slice 4) reads
-- {"op":"<and|or|not>","args":[...]} as the canonical boolean node and
-- {"flag":"<name>"} as the leaf. Single-flag unwrap is a parse-time
-- shortcut equivalent to a 1-arg AND. The short {"and":[...]} form in
-- msg 1746 would require a per-key parser that doesn't generalise to
-- OR / NOT. Design §2.4 long form is the load-bearing decision.
--
-- Live-data expected delta (172 rules total):
--
--   ['with_ccr']                 ×  5 rows → {"flag":"with_ccr"}
--   ['with_amend']               ×  4 rows → {"flag":"with_amend"}
--   ['with_cci']                 ×  4 rows → {"flag":"with_cci"}
--   ['with_ccr', 'with_amend']   ×  4 rows → {"op":"and","args":[
--                                              {"flag":"with_ccr"},
--                                              {"flag":"with_amend"}
--                                            ]}
--   NULL or {}                   × 155 rows → stays NULL
--
--   Total touched: 17 rows.
--
-- Idempotent: WHERE condition_expr IS NULL guards re-runs against
-- double-writing audit rows for already-translated rules.
--
-- jsonb construction: jsonb_build_object + jsonb_agg + a CASE on
-- array_length keeps the long-form / unwrapped-flag split inline in
-- one UPDATE. Per-flag jsonb leaf is built by a LATERAL unnest over
-- the flag array so the args[] order matches the source array.

SELECT set_config(
    'paliad.audit_reason',
    'backfill 084: condition_expr from condition_flag text[] per design §2.4 — '
        || 'single flag unwrapped, multi flag long form {op:and, args:[...]}',
    true);

UPDATE paliad.deadline_rules dr
   SET condition_expr = sub.expr
  FROM (
        SELECT dr_inner.id AS rule_id,
               CASE
                   -- Single flag: unwrapped leaf.
                   WHEN array_length(dr_inner.condition_flag, 1) = 1
                       THEN jsonb_build_object('flag', dr_inner.condition_flag[1])

                   -- >=2 flags: long-form AND with args[] preserving order.
                   WHEN array_length(dr_inner.condition_flag, 1) >= 2
                       THEN jsonb_build_object(
                           'op',   'and',
                           'args', (
                               SELECT jsonb_agg(jsonb_build_object('flag', f) ORDER BY ord)
                                 FROM unnest(dr_inner.condition_flag) WITH ORDINALITY AS u(f, ord)
                           )
                       )

                   -- Empty array (array_length=0) or NULL: leave NULL.
                   ELSE NULL
               END AS expr
          FROM paliad.deadline_rules dr_inner
         WHERE dr_inner.condition_flag IS NOT NULL
           AND array_length(dr_inner.condition_flag, 1) > 0
  ) AS sub
 WHERE dr.id = sub.rule_id
   AND dr.condition_expr IS NULL;

DO $$
DECLARE
    n_total      int;
    n_with_flag  int;
    n_with_expr  int;
    n_with_both  int;
BEGIN
    SELECT count(*),
           count(*) FILTER (WHERE condition_flag IS NOT NULL AND array_length(condition_flag, 1) > 0),
           count(*) FILTER (WHERE condition_expr IS NOT NULL),
           count(*) FILTER (WHERE condition_flag IS NOT NULL AND array_length(condition_flag, 1) > 0
                                AND condition_expr IS NOT NULL)
      INTO n_total, n_with_flag, n_with_expr, n_with_both
      FROM paliad.deadline_rules;
    RAISE NOTICE 'backfill 084: total=%, with_condition_flag=%, with_condition_expr=%, both=%',
                 n_total, n_with_flag, n_with_expr, n_with_both;
    -- Hard assertion: every rule with a non-empty condition_flag now
    -- has a non-NULL condition_expr (the inverse of the legacy column).
    IF n_with_flag <> n_with_both THEN
        RAISE EXCEPTION 'backfill 084: % rules carry condition_flag but no condition_expr — '
                        'translation incomplete',
                        n_with_flag - n_with_both;
    END IF;
END $$;
