esphome:
  debug_scheduler: true  # Enable scheduler leak detection
  name: scheduler-self-keyed-test
  on_boot:
    priority: -100
    then:
      - logger.log: "Starting scheduler self-keyed tests"

host:
api:
logger:
  level: VERBOSE

globals:
  - id: tests_done
    type: bool
    initial_value: 'false'

script:
  - id: test_self_keyed
    then:
      - logger.log: "Testing self-keyed scheduler API"
      - lambda: |-
          // Two distinct keys backed by addresses of static markers — they
          // must not collide even though both are self-keyed and share no
          // Component pointer. Static storage gives them stable, unique
          // addresses for the lifetime of the program.
          static int key_a_marker = 0;
          static int key_b_marker = 0;
          void *key_a = &key_a_marker;
          void *key_b = &key_b_marker;

          // ---- Test 1: Self-keyed timeout fires ----
          App.scheduler.set_timeout(key_a, 50, []() {
            ESP_LOGI("test", "Self timeout A fired");
          });

          // ---- Test 2: Self-keyed cancel cancels only that key ----
          App.scheduler.set_timeout(key_b, 100, []() {
            ESP_LOGE("test", "ERROR: Self timeout B should have been cancelled");
          });
          App.scheduler.cancel_timeout(key_b);

          // ---- Test 3: Two independent self keys don't collide ----
          // Using fresh static markers so neither matches key_a / key_b.
          static int key_c_marker = 0;
          static int key_d_marker = 0;
          void *key_c = &key_c_marker;
          void *key_d = &key_d_marker;
          App.scheduler.set_timeout(key_c, 150, []() {
            ESP_LOGI("test", "Self timeout C fired");
          });
          App.scheduler.set_timeout(key_d, 150, []() {
            ESP_LOGI("test", "Self timeout D fired");
          });

          // ---- Test 4: Self-keyed and component-keyed don't collide ----
          // Use a self pointer that happens to look like a Component-attached id.
          // The scheduler must treat them as separate namespaces.
          static int shared_marker = 0;
          void *self_shared = &shared_marker;
          App.scheduler.set_timeout(self_shared, 200, []() {
            ESP_LOGI("test", "Self timeout shared fired");
          });
          App.scheduler.set_timeout(id(test_sensor), 7777U, 200, []() {
            ESP_LOGI("test", "Component timeout 7777 fired");
          });

          // ---- Test 5: Self-keyed interval fires multiple times then cancels ----
          static int interval_count = 0;
          static int key_e_marker = 0;
          void *key_e = &key_e_marker;
          App.scheduler.set_interval(key_e, 80, [key_e]() {
            interval_count++;
            if (interval_count == 2) {
              ESP_LOGI("test", "Self interval E fired twice");
              App.scheduler.cancel_interval(key_e);
            }
          });

          // ---- Test 6: Re-registering same self-key replaces the timer ----
          // The old timer must NOT fire; only the new one does.
          static int key_f_marker = 0;
          void *key_f = &key_f_marker;
          App.scheduler.set_timeout(key_f, 250, []() {
            ESP_LOGE("test", "ERROR: Self timeout F first registration should have been replaced");
          });
          App.scheduler.set_timeout(key_f, 300, []() {
            ESP_LOGI("test", "Self timeout F replacement fired");
          });

          // Log completion after all timers should have fired
          App.scheduler.set_timeout(id(test_sensor), 9999U, 1500, []() {
            ESP_LOGI("test", "All self-keyed tests complete");
          });

sensor:
  - platform: template
    name: Test Sensor
    id: test_sensor
    lambda: return 1.0;
    update_interval: never

interval:
  - interval: 0.1s
    then:
      - if:
          condition:
            lambda: 'return id(tests_done) == false;'
          then:
            - lambda: 'id(tests_done) = true;'
            - script.execute: test_self_keyed
