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

# VirtualDataDocumentLookup Test Suite
# Tests the four cases of virtual data document lookup:
# 1. All components consumed and rule index found -> evaluate rule
# 2. Rule index found with remaining components -> evaluate rule then index result
# 3. All components consumed but undefined -> apply components to data directly  
# 4. Subobject found -> panic (not yet implemented)

cases:
  # Case 1: All components consumed and rule index found
  - note: rule_index_all_components_consumed
    data:
      users:
        alice: {"name": "Alice", "age": 30}
    input:
      rule_name: "alice_profile"
    modules:
      - |
        package test.users
        alice_profile := data.users.alice
      - |
        package test
        main := result if {
          result := data.test.users[input.rule_name]
        }
    query: data.test.main
    want_result: {"name": "Alice", "age": 30}
    allow_interpreter_incorrect_behavior: true

  # Case 2: Rule index found with remaining components  
  - note: rule_index_with_remaining_components
    data:
      users:
        alice: {"name": "Alice", "age": 30, "profile": {"bio": "Software Engineer"}}
    input:
      rule_name: "alice_data"
      field1: "profile"
      field2: "bio"
    modules:
      - |
        package test.users
        alice_data := data.users.alice
      - |
        package test
        main := result if {
          result := data.test.users[input.rule_name][input.field1][input.field2]
        }
    query: data.test.main
    want_result: "Software Engineer"
    allow_interpreter_incorrect_behavior: true

  # Case 2b: Rule index with multiple remaining components
  - note: rule_index_multiple_remaining_components
    data:
      config:
        app:
          settings: {"theme": "dark", "lang": "en"}
    input:
      rule: "app_config"
      path1: "settings"
      path2: "theme"
    modules:
      - |
        package test.config
        app_config := data.config.app
      - |
        package test
        main := result if {
          result := data.test.config[input.rule][input.path1][input.path2]
        }
    query: data.test.main
    want_result: "dark"
    allow_interpreter_incorrect_behavior: true

  # Case 3: All components consumed but no rule exists (direct data access)
  - note: direct_data_access_no_rules
    data:
      users:
        bob: {"name": "Bob", "age": 25}
    input:
      person: "bob"
      attribute: "name"
    modules:
      - |
        package test
        main := result if {
          # No rule defined for data.users.bob, should access data directly
          result := data.users[input.person][input.attribute]
        }
    query: data.test.main
    want_result: "Bob"

  # Case 3b: Direct nested data access (no rules)
  - note: direct_nested_data_access
    data:
      system:
        metrics:
          cpu: 85
          memory: 70
    input:
      metric: "cpu"
    modules:
      - |
        package test
        main := result if {
          # No rules defined for data.system.metrics, access data directly
          result := data.system.metrics[input.metric]
        }
    query: data.test.main
    want_result: 85

  # Case 3c: Direct array indexing (no rules)  
  - note: direct_array_indexing
    data:
      inventory:
        fruits: ["apple", "banana", "cherry"]
    input:
      collection: "fruits"
      index: 1
    modules:
      - |
        package test
        main := result if {
          # No rules defined for data.inventory.fruits, access array directly
          result := data.inventory[input.collection][input.index]
        }
    query: data.test.main
    want_result: "banana"

  # Mixed case: Rule exists at intermediate level, then data access
  - note: rule_at_intermediate_level
    data:
      company:
        employees: 
          - {"name": "Alice", "dept": "Engineering"}
          - {"name": "Bob", "dept": "Marketing"}
    input:
      rule_name: "staff"
      idx: 0
      field: "name"
    modules:
      - |
        package test.company
        staff := data.company.employees
      - |
        package test  
        main := result if {
          # Rule exists at data.test.company.staff, then access array element
          result := data.test.company[input.rule_name][input.idx][input.field]
        }
    query: data.test.main
    want_result: "Alice"
    allow_interpreter_incorrect_behavior: true

  # Case with dynamic indexing via register
  - note: rule_with_dynamic_indexing
    data:
      products:
        electronics: {"laptop": 1200, "phone": 800}
        clothing: {"shirt": 25, "pants": 50}
    input:
      rule: "electronics_catalog"
      item: "laptop"
    modules:
      - |
        package test.products
        electronics_catalog := data.products.electronics
      - |
        package test
        main := result if {
          result := data.test.products[input.rule][input.item]
        }
    query: data.test.main
    want_result: 1200
    allow_interpreter_incorrect_behavior: true

  # Case 3d: Undefined path access returns undefined
  - note: undefined_path_access
    data: {}
    input:
      path2: "path"
      path3: "value"
    modules:
      - |
        package test
        main := result if {
          # This should access undefined data, returning undefined
          result := data.nonexistent[input.path2][input.path3]
        }
    query: data.test.main
    want_result: "#undefined"

  # Case that actually triggers VirtualDataDocumentLookup:
  # Path is a prefix of multiple rules
  - note: virtual_lookup_with_rule_prefix
    data:
      config:
        app: {"name": "MyApp", "version": "1.0"}
    input:
      submodule: "app"
      field: "name"
    modules:
      - |
        package test.config.app
        name := data.config.app.name
        version := data.config.app.version
        full_info := {"name": data.config.app.name, "version": data.config.app.version}
      - |
        package test
        main := result if {
          # This should trigger VirtualDataDocumentLookup since data.test.config.app
          # is a prefix of multiple rules: data.test.config.app.name, data.test.config.app.version, etc.
          result := data.test.config[input.submodule][input.field]
        }
    query: data.test.main
    want_result: "MyApp"

  # Case 4: Subobject case - evaluate all rules in a subobject and merge with data
  - note: subobject_case_multiple_rules
    data:
      users:
        alice: {"name": "Alice", "age": 30}
        bob: {"name": "Bob", "age": 25} 
      permissions:
        alice: {"admin": true}
        bob: {"admin": false}
    modules:
      - |
        package test.users.alice
        profile := {"name": data.users.alice.name, "age": data.users.alice.age}
        is_admin := data.permissions.alice.admin
      - |
        package test.users.bob  
        profile := {"name": data.users.bob.name, "age": data.users.bob.age}
        is_admin := data.permissions.bob.admin
      - |
        package test
        main := result if {
          # This should trigger Case 4: all components consumed and we have a subobject
          # data.test.users should contain the evaluated rules from both alice and bob packages
          result := data.test.users
        }
    query: data.test.main
    want_result:
      alice:
        profile: {"name": "Alice", "age": 30}
        is_admin: true
      bob:
        profile: {"name": "Bob", "age": 25}
        is_admin: false

  # Case 4b: Nested subobject evaluation with cache hits
  - note: nested_subobject_with_cache_hits
    data:
      company:
        departments:
          engineering: {"budget": 1000000}
          marketing: {"budget": 500000}
        employees:
          alice: {"dept": "engineering", "salary": 100000}
          bob: {"dept": "marketing", "salary": 70000}
          charlie: {"dept": "engineering", "salary": 90000}
    modules:
      - |
        package test.company.departments.engineering
        total_budget := data.company.departments.engineering.budget
        employee_count := count([e | e := data.company.employees[_]; e.dept == "engineering"])
        avg_budget_per_employee := total_budget / employee_count
      - |
        package test.company.departments.marketing  
        total_budget := data.company.departments.marketing.budget
        employee_count := count([e | e := data.company.employees[_]; e.dept == "marketing"])
        avg_budget_per_employee := total_budget / employee_count
      - |
        package test.company.employees.alice
        profile := data.company.employees.alice
        department_info := data.test.company.departments[profile.dept]  # Should hit cache
      - |
        package test.company.employees.bob
        profile := data.company.employees.bob  
        department_info := data.test.company.departments[profile.dept]  # Should hit cache
      - |
        package test.company.employees.charlie
        profile := data.company.employees.charlie
        department_info := data.test.company.departments[profile.dept]  # Should hit cache again
      - |
        package test
        main := result if {
          # This creates nested subobject evaluations:
          # 1. data.test.company (subobject with departments and employees)
          # 2. data.test.company.departments (subobject with engineering and marketing)  
          # 3. data.test.company.employees (subobject with alice, bob, charlie)
          # The departments should be cached and reused multiple times
          result := {
            "company_overview": data.test.company,
            "departments_only": data.test.company.departments,  # Cache hit for departments
            "employees_only": data.test.company.employees,      # Cache hit for employees  
            "engineering_dept": data.test.company.departments.engineering  # Cache hit for specific dept
          }
        }
    query: data.test.main
    want_result:
      company_overview:
        departments:
          engineering:
            total_budget: 1000000
            employee_count: 2
            avg_budget_per_employee: 500000
          marketing:
            total_budget: 500000  
            employee_count: 1
            avg_budget_per_employee: 500000
        employees:
          alice:
            profile: {"dept": "engineering", "salary": 100000}
            department_info:
              total_budget: 1000000
              employee_count: 2
              avg_budget_per_employee: 500000
          bob:
            profile: {"dept": "marketing", "salary": 70000}
            department_info:
              total_budget: 500000
              employee_count: 1
              avg_budget_per_employee: 500000
          charlie:
            profile: {"dept": "engineering", "salary": 90000}  
            department_info:
              total_budget: 1000000
              employee_count: 2
              avg_budget_per_employee: 500000
      departments_only:
        engineering:
          total_budget: 1000000
          employee_count: 2
          avg_budget_per_employee: 500000
        marketing:
          total_budget: 500000
          employee_count: 1
          avg_budget_per_employee: 500000
      employees_only:
        alice:
          profile: {"dept": "engineering", "salary": 100000}
          department_info:
            total_budget: 1000000
            employee_count: 2
            avg_budget_per_employee: 500000
        bob:
          profile: {"dept": "marketing", "salary": 70000}
          department_info:
            total_budget: 500000
            employee_count: 1
            avg_budget_per_employee: 500000
        charlie:
          profile: {"dept": "engineering", "salary": 90000}
          department_info:
            total_budget: 1000000
            employee_count: 2
            avg_budget_per_employee: 500000
      engineering_dept:
        total_budget: 1000000
        employee_count: 2
        avg_budget_per_employee: 500000

  # Test that function rules are excluded from virtual data document lookup
  - note: function_rules_excluded_from_virtual_lookup
    data:
      config:
        app_name: "TestApp"
        version: "1.0.0"
    modules:
      - |
        package test.config
        # Regular rule - should be accessible via virtual lookup
        application_info := {"name": data.config.app_name, "version": data.config.version}
        
        # Function rule - should NOT be accessible via virtual lookup
        format_version(major, minor) := sprintf("%d.%d", [major, minor])
        
        # Another regular rule - should be accessible
        app_status := "running"
      - |
        package test
        main := result if {
          # This should only include regular rules, not function rules
          # data.test.config should contain: application_info, app_status
          # but NOT: format_version (because it's a function rule)
          result := data.test.config
        }
    query: data.test.main
    want_result:
      application_info: {"name": "TestApp", "version": "1.0.0"}
      app_status: "running"
      # Note: format_version should NOT appear here since it's a function rule