///- --package .
:# scenario static =
    :< testing

:< sm'_operators

:== order = { name : string; count : i32 }

:| mix =
    | Mix : list order
    | BadOrder : string

:= named_count (name : string) (count : string) : option order =
    ?= convert::try_convert (sm'::trim count) ?|
    | Some n => Some { name = sm'::trim name; count = n }
    | None => None

:= order_of (part : string) : option order =
    := fields = part |> sm'::split ":" |> array
    ? am'::length fields <> 2 ?> None |: named_count (am'::index 0 fields) (am'::index 1 fields)

:~ ^ collected (parts : array string) (i : i32) (orders : list order) : mix =
    ? i < 0 ?> Mix orders
    |:
        := part = am'::index i parts
        ?= order_of part ?|
        | Some o => collected parts (i - 1) (o :: orders)
        | None => BadOrder part

:= mix_of (monster : string) (count : i32) : mix =
    ? sm'::contains ":" monster ?>
        := parts = monster |> sm'::split "," |> array
        collected parts (am'::length parts - 1) []
    |: Mix [ { name = monster; count = count } ]

:= order_entry (radius : i32) (o : order) : string = o::name ++# "," ++#? o::count ++# ",," ++#? radius

:= summon_orders (orders : list order) (radius : i32) : string =
    ?= orders ?|
    | head :: tail => listm::fold (\ text o => text ++# ";" ++# order_entry radius o) (order_entry radius head) tail
    | [] => ""

:= total_count (orders : list order) : i32 = listm::fold (\ total (o : order) => total + o::count) 0 orders

:= camp_orders (m : mix) (radius : i32) : string * i32 =
    ?= m ?|
    | Mix orders => summon_orders orders radius, total_count orders
    | BadOrder part => failwith part

///> lua
///> gleam
:# a_single_monster_gives_the_harness_order =
    := ~radius = 2i32
    camp_orders (mix_of "troll" 3) radius |> _assert_eq ("troll,3,,2", 3i32)

///> lua
///> gleam
:# a_mixed_camp_gives_one_order_per_monster =
    := ~radius = 2i32
    camp_orders (mix_of "troll:2, cyclops:1" 0) radius |> _assert_eq ("troll,2,,2;cyclops,1,,2", 3i32)

///> lua
///> gleam
:# a_count_that_is_not_a_number_names_its_part =
    := ~monster = "troll:2,cyclops:x"
    mix_of monster 0 |> _assert_eq (BadOrder "cyclops:x")
