# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.

# Object Operations Test Suite
# Tests advanced object creation, manipulation, and edge cases
# Covers dynamic keys, collisions, non-string keys, and template validation

cases:
  - note: object_key_collision_overwrite
    description: Setting same key twice should overwrite the value
    example_rego: "{\"key\": 1, \"key\": 2}"
    literals:
      - {}
      - "key"
      - 1
      - 2
    instruction_params:
      object_create_params:
        - dest: 0
          template_literal_idx: 0
          literal_key_fields: []
          fields: []
    instructions:
      - "ObjectCreate { params_index: 0 }"
      - "Load { dest: 1, literal_idx: 1 }"  # key
      - "Load { dest: 2, literal_idx: 2 }"  # value 1
      - "ObjectSet { obj: 0, key: 1, value: 2 }"
      - "Load { dest: 3, literal_idx: 3 }"  # value 2
      - "ObjectSet { obj: 0, key: 1, value: 3 }"  # Overwrite
      - "Return { value: 0 }"
    want_result: {"key": 2}

  - note: object_dynamic_key_generation
    description: Generate object keys dynamically from loop iteration
    example_rego: "{sprintf(\"key_%d\", [i]): i | i := [1, 2][_]}"
    literals:
      - {}
      - 1
      - 2
      - "key_1"
      - "key_2"
    instruction_params:
      object_create_params:
        - dest: 0
          template_literal_idx: 0
          literal_key_fields: []
          fields: []
      loop_params:
        - mode: "ForEach"
          collection: 5
          key_reg: 10
          value_reg: 11
          result_reg: 12
          body_start: 7
          loop_end: 13
    instructions:
      - "ObjectCreate { params_index: 0 }"
      - "ArrayNew { dest: 5 }"
      - "Load { dest: 1, literal_idx: 1 }"
      - "ArrayPush { arr: 5, value: 1 }"
      - "Load { dest: 2, literal_idx: 2 }"
      - "ArrayPush { arr: 5, value: 2 }"
      - "LoopStart { params_index: 0 }"
      # Generate dynamic key based on value
      - "Load { dest: 20, literal_idx: 3 }"  # "key_1" when i=1
      - "Load { dest: 21, literal_idx: 4 }"  # "key_2" when i=2
      # For simplicity, we'll set both keys
      - "ObjectSet { obj: 0, key: 20, value: 1 }"
      - "Load { dest: 3, literal_idx: 2 }"
      - "ObjectSet { obj: 0, key: 21, value: 3 }"
      - "LoopNext { body_start: 7, loop_end: 13 }"
      - "Return { value: 0 }"
    want_result: {"key_1": 1, "key_2": 2}

  - note: object_number_keys
    description: Object with number keys
    example_rego: "{1: \"one\", 2: \"two\", 42: \"answer\"}"
    literals:
      - {}
      - 1
      - "one"
      - 2
      - "two"
      - 42
      - "answer"
    instruction_params:
      object_create_params:
        - dest: 0
          template_literal_idx: 0
          literal_key_fields: []
          fields: []
    instructions:
      - "ObjectCreate { params_index: 0 }"
      - "Load { dest: 1, literal_idx: 1 }"  # key 1
      - "Load { dest: 2, literal_idx: 2 }"  # value "one"
      - "ObjectSet { obj: 0, key: 1, value: 2 }"
      - "Load { dest: 3, literal_idx: 3 }"  # key 2
      - "Load { dest: 4, literal_idx: 4 }"  # value "two"
      - "ObjectSet { obj: 0, key: 3, value: 4 }"
      - "Load { dest: 5, literal_idx: 5 }"  # key 42
      - "Load { dest: 6, literal_idx: 6 }"  # value "answer"
      - "ObjectSet { obj: 0, key: 5, value: 6 }"
      - "Return { value: 0 }"
    want_result: {1: "one", 2: "two", 42: "answer"}

  - note: object_boolean_keys
    description: Object with boolean keys
    example_rego: "{true: \"yes\", false: \"no\"}"
    literals:
      - {}
      - "yes"
      - "no"
    instruction_params:
      object_create_params:
        - dest: 0
          template_literal_idx: 0
          literal_key_fields: []
          fields: []
    instructions:
      - "ObjectCreate { params_index: 0 }"
      - "LoadTrue { dest: 1 }"
      - "Load { dest: 2, literal_idx: 1 }"  # value "yes"
      - "ObjectSet { obj: 0, key: 1, value: 2 }"
      - "LoadFalse { dest: 3 }"
      - "Load { dest: 4, literal_idx: 2 }"  # value "no"
      - "ObjectSet { obj: 0, key: 3, value: 4 }"
      - "Return { value: 0 }"
    want_result: {true: "yes", false: "no"}

  - note: object_null_key
    description: Object with null key
    example_rego: "{null: \"nothing\"}"
    literals:
      - {}
      - "nothing"
    instruction_params:
      object_create_params:
        - dest: 0
          template_literal_idx: 0
          literal_key_fields: []
          fields: []
    instructions:
      - "ObjectCreate { params_index: 0 }"
      - "LoadNull { dest: 1 }"
      - "Load { dest: 2, literal_idx: 1 }"
      - "ObjectSet { obj: 0, key: 1, value: 2 }"
      - "Return { value: 0 }"
    want_result: {null: "nothing"}

  - note: object_empty_string_key
    description: Object with empty string key
    example_rego: "{\"\": \"empty_key\"}"
    literals:
      - {}
      - ""
      - "empty_key"
    instruction_params:
      object_create_params:
        - dest: 0
          template_literal_idx: 0
          literal_key_fields: []
          fields: []
    instructions:
      - "ObjectCreate { params_index: 0 }"
      - "Load { dest: 1, literal_idx: 1 }"  # empty string key
      - "Load { dest: 2, literal_idx: 2 }"  # value
      - "ObjectSet { obj: 0, key: 1, value: 2 }"
      - "Return { value: 0 }"
    want_result: {"": "empty_key"}

  - note: object_set_undefined_value
    description: Setting undefined value in object keeps it
    literals:
      - {}
      - "key"
    instruction_params:
      object_create_params:
        - dest: 0
          template_literal_idx: 0
          literal_key_fields: []
          fields: []
    instructions:
      - "ObjectCreate { params_index: 0 }"
      - "Load { dest: 1, literal_idx: 1 }"
      # r2 is undefined (never loaded)
      - "ObjectSet { obj: 0, key: 1, value: 2 }"
      - "Return { value: 0 }"
    want_result: {"key": "#undefined"}

  - note: object_create_with_template
    description: ObjectCreate with pre-populated template
    literals:
      - {"existing": "value", "num": 42}
      - "new_key"
      - "new_value"
    instruction_params:
      object_create_params:
        - dest: 0
          template_literal_idx: 0
          literal_key_fields: []
          fields: []
    instructions:
      - "ObjectCreate { params_index: 0 }"
      - "Load { dest: 1, literal_idx: 1 }"
      - "Load { dest: 2, literal_idx: 2 }"
      - "ObjectSet { obj: 0, key: 1, value: 2 }"
      - "Return { value: 0 }"
    want_result: {"existing": "value", "num": 42, "new_key": "new_value"}

  - note: object_nested_structure
    description: Create deeply nested object structure
    example_rego: "{\"outer\": {\"middle\": {\"inner\": \"value\"}}}"
    literals:
      - {}
      - "outer"
      - "middle"
      - "inner"
      - "value"
    instruction_params:
      object_create_params:
        - dest: 0
          template_literal_idx: 0
          literal_key_fields: []
          fields: []
    instructions:
      - "ObjectCreate { params_index: 0 }"  # outer
      - "Load { dest: 10, literal_idx: 0 }"  # middle object
      - "Load { dest: 11, literal_idx: 0 }"  # inner object
      - "Load { dest: 12, literal_idx: 3 }"  # "inner" key
      - "Load { dest: 13, literal_idx: 4 }"  # "value"
      - "ObjectSet { obj: 11, key: 12, value: 13 }"  # {inner: "value"}
      - "Load { dest: 14, literal_idx: 2 }"  # "middle" key
      - "ObjectSet { obj: 10, key: 14, value: 11 }"  # {middle: {...}}
      - "Load { dest: 15, literal_idx: 1 }"  # "outer" key
      - "ObjectSet { obj: 0, key: 15, value: 10 }"  # {outer: {...}}
      - "Return { value: 0 }"
    want_result: {"outer": {"middle": {"inner": "value"}}}

  - note: object_mixed_value_types
    description: Object with values of different types
    example_rego: "{\"str\": \"text\", \"num\": 42, \"bool\": true, \"null\": null, \"arr\": [1, 2]}"
    literals:
      - {}
      - "str"
      - "text"
      - "num"
      - 42
      - "bool"
      - "null"
      - "arr"
      - [1, 2]
    instruction_params:
      object_create_params:
        - dest: 0
          template_literal_idx: 0
          literal_key_fields: []
          fields: []
    instructions:
      - "ObjectCreate { params_index: 0 }"
      - "Load { dest: 1, literal_idx: 1 }"  # "str"
      - "Load { dest: 2, literal_idx: 2 }"  # "text"
      - "ObjectSet { obj: 0, key: 1, value: 2 }"
      - "Load { dest: 3, literal_idx: 3 }"  # "num"
      - "Load { dest: 4, literal_idx: 4 }"  # 42
      - "ObjectSet { obj: 0, key: 3, value: 4 }"
      - "Load { dest: 5, literal_idx: 5 }"  # "bool"
      - "LoadTrue { dest: 6 }"
      - "ObjectSet { obj: 0, key: 5, value: 6 }"
      - "Load { dest: 7, literal_idx: 6 }"  # "null"
      - "LoadNull { dest: 8 }"
      - "ObjectSet { obj: 0, key: 7, value: 8 }"
      - "Load { dest: 9, literal_idx: 7 }"  # "arr"
      - "Load { dest: 10, literal_idx: 8 }"  # [1, 2]
      - "ObjectSet { obj: 0, key: 9, value: 10 }"
      - "Return { value: 0 }"
    want_result: {"str": "text", "num": 42, "bool": true, "null": null, "arr": [1, 2]}
