esphome:
  debug_scheduler: true  # Enable scheduler leak detection
  name: scheduler-retry-test
  on_boot:
    priority: -100
    then:
      - logger.log: "Starting scheduler retry tests"
      # Run all tests sequentially with delays
      - script.execute: run_all_tests

host:
api:
logger:
  level: VERY_VERBOSE

globals:
  - id: simple_retry_counter
    type: int
    initial_value: '0'
  - id: backoff_retry_counter
    type: int
    initial_value: '0'
  - id: backoff_last_attempt_time
    type: uint32_t
    initial_value: '0'
  - id: immediate_done_counter
    type: int
    initial_value: '0'
  - id: cancel_retry_counter
    type: int
    initial_value: '0'
  - id: empty_name_retry_counter
    type: int
    initial_value: '0'
  - id: script_retry_counter
    type: int
    initial_value: '0'
  - id: multiple_same_name_counter
    type: int
    initial_value: '0'
  - id: const_char_retry_counter
    type: int
    initial_value: '0'
  - id: static_char_retry_counter
    type: int
    initial_value: '0'

# Using different component types for each test to ensure isolation
sensor:
  - platform: template
    name: Simple Retry Test Sensor
    id: simple_retry_sensor
    lambda: return 1.0;
    update_interval: never

  - platform: template
    name: Backoff Retry Test Sensor
    id: backoff_retry_sensor
    lambda: return 2.0;
    update_interval: never

  - platform: template
    name: Immediate Done Test Sensor
    id: immediate_done_sensor
    lambda: return 3.0;
    update_interval: never

binary_sensor:
  - platform: template
    name: Cancel Retry Test Binary Sensor
    id: cancel_retry_binary_sensor
    lambda: return false;

  - platform: template
    name: Empty Name Test Binary Sensor
    id: empty_name_binary_sensor
    lambda: return true;

switch:
  - platform: template
    name: Script Retry Test Switch
    id: script_retry_switch
    optimistic: true

  - platform: template
    name: Multiple Same Name Test Switch
    id: multiple_same_name_switch
    optimistic: true

script:
  - id: run_all_tests
    then:
      # Test 1: Simple retry
      - logger.log: "=== Test 1: Simple retry ==="
      - lambda: |-
          auto *component = id(simple_retry_sensor);
          App.scheduler.set_retry(component, "simple_retry", 50, 3,
            [](uint8_t retry_countdown) {
              id(simple_retry_counter)++;
              ESP_LOGI("test", "Simple retry attempt %d (countdown=%d)",
                       id(simple_retry_counter), retry_countdown);

              if (id(simple_retry_counter) >= 2) {
                ESP_LOGI("test", "Simple retry succeeded on attempt %d", id(simple_retry_counter));
                return RetryResult::DONE;
              }
              return RetryResult::RETRY;
            });

      # Test 2: Backoff retry
      - logger.log: "=== Test 2: Retry with backoff ==="
      - lambda: |-
          auto *component = id(backoff_retry_sensor);

          App.scheduler.set_retry(component, "backoff_retry", 50, 4,
            [](uint8_t retry_countdown) {
              id(backoff_retry_counter)++;
              uint32_t now = millis();
              uint32_t interval = 0;

              // Only calculate interval after first attempt
              if (id(backoff_retry_counter) > 1) {
                interval = now - id(backoff_last_attempt_time);
              }
              id(backoff_last_attempt_time) = now;

              ESP_LOGI("test", "Backoff retry attempt %d (countdown=%d, interval=%dms)",
                       id(backoff_retry_counter), retry_countdown, interval);

              if (id(backoff_retry_counter) == 1) {
                ESP_LOGI("test", "First call was immediate");
              } else if (id(backoff_retry_counter) == 2) {
                ESP_LOGI("test", "Second call interval: %dms (expected ~50ms)", interval);
              } else if (id(backoff_retry_counter) == 3) {
                ESP_LOGI("test", "Third call interval: %dms (expected ~100ms)", interval);
              } else if (id(backoff_retry_counter) == 4) {
                ESP_LOGI("test", "Fourth call interval: %dms (expected ~200ms)", interval);
                ESP_LOGI("test", "Backoff retry completed");
                return RetryResult::DONE;
              }

              return RetryResult::RETRY;
            }, 2.0f);

      # Test 3: Immediate done
      - logger.log: "=== Test 3: Immediate done ==="
      - lambda: |-
          auto *component = id(immediate_done_sensor);
          App.scheduler.set_retry(component, "immediate_done", 50, 5,
            [](uint8_t retry_countdown) {
              id(immediate_done_counter)++;
              ESP_LOGI("test", "Immediate done retry called (countdown=%d)", retry_countdown);
              return RetryResult::DONE;
            });

      # Test 4: Cancel retry
      - logger.log: "=== Test 4: Cancel retry ==="
      - lambda: |-
          auto *component = id(cancel_retry_binary_sensor);
          App.scheduler.set_retry(component, "cancel_test", 30, 10,
            [](uint8_t retry_countdown) {
              id(cancel_retry_counter)++;
              ESP_LOGI("test", "Cancel test retry attempt %d", id(cancel_retry_counter));
              return RetryResult::RETRY;
            });

          // Cancel it after 100ms
          App.scheduler.set_timeout(component, "cancel_timer", 100, []() {
            bool cancelled = App.scheduler.cancel_retry(id(cancel_retry_binary_sensor), "cancel_test");
            ESP_LOGI("test", "Retry cancellation result: %s", cancelled ? "true" : "false");
            ESP_LOGI("test", "Cancel retry ran %d times before cancellation", id(cancel_retry_counter));
          });

      # Test 5: Empty name retry
      - logger.log: "=== Test 5: Empty name retry ==="
      - lambda: |-
          auto *component = id(empty_name_binary_sensor);
          App.scheduler.set_retry(component, "", 100, 5,
            [](uint8_t retry_countdown) {
              id(empty_name_retry_counter)++;
              ESP_LOGI("test", "Empty name retry attempt %d", id(empty_name_retry_counter));
              return RetryResult::RETRY;
            });

          // Try to cancel after 150ms
          App.scheduler.set_timeout(component, "empty_cancel_timer", 150, []() {
            bool cancelled = App.scheduler.cancel_retry(id(empty_name_binary_sensor), "");
            ESP_LOGI("test", "Empty name retry cancel result: %s",
                     cancelled ? "true" : "false");
            ESP_LOGI("test", "Empty name retry ran %d times", id(empty_name_retry_counter));
          });

      # Test 6: Component method
      - logger.log: "=== Test 6: Component::set_retry method ==="
      - lambda: |-
          class TestRetryComponent : public Component {
          public:
            void test_retry() {
              this->set_retry(50, 3,
                [](uint8_t retry_countdown) {
                  id(script_retry_counter)++;
                  ESP_LOGI("test", "Component retry attempt %d", id(script_retry_counter));
                  if (id(script_retry_counter) >= 2) {
                    return RetryResult::DONE;
                  }
                  return RetryResult::RETRY;
                }, 1.5f);
            }
          };

          static TestRetryComponent test_component;
          test_component.test_retry();

      # Test 7: Multiple same name
      - logger.log: "=== Test 7: Multiple retries with same name ==="
      - lambda: |-
          auto *component = id(multiple_same_name_switch);

          // Set first retry
          App.scheduler.set_retry(component, "duplicate_retry", 100, 5,
            [](uint8_t retry_countdown) {
              id(multiple_same_name_counter) += 1;
              ESP_LOGI("test", "First duplicate retry - should not run");
              return RetryResult::RETRY;
            });

          // Set second retry with same name (should cancel first)
          App.scheduler.set_retry(component, "duplicate_retry", 50, 3,
            [](uint8_t retry_countdown) {
              id(multiple_same_name_counter) += 10;
              ESP_LOGI("test", "Second duplicate retry attempt (counter=%d)",
                       id(multiple_same_name_counter));
              if (id(multiple_same_name_counter) >= 20) {
                return RetryResult::DONE;
              }
              return RetryResult::RETRY;
            });

      # Test 8: Const char* overloads
      - logger.log: "=== Test 8: Const char* overloads ==="
      - lambda: |-
          auto *component = id(simple_retry_sensor);

          // Test 8a: Direct string literal
          App.scheduler.set_retry(component, "const_char_test", 30, 2,
            [](uint8_t retry_countdown) {
              id(const_char_retry_counter)++;
              ESP_LOGI("test", "Const char retry %d", id(const_char_retry_counter));
              return RetryResult::DONE;
            });

      # Test 9: Static const char* variable
      - logger.log: "=== Test 9: Static const char* ==="
      - lambda: |-
          auto *component = id(backoff_retry_sensor);

          static const char* STATIC_NAME = "static_retry_test";
          App.scheduler.set_retry(component, STATIC_NAME, 20, 1,
            [](uint8_t retry_countdown) {
              id(static_char_retry_counter)++;
              ESP_LOGI("test", "Static const char retry %d", id(static_char_retry_counter));
              return RetryResult::DONE;
            });

          // Cancel with same static const char*
          App.scheduler.set_timeout(component, "static_cancel", 10, []() {
            static const char* STATIC_NAME = "static_retry_test";
            bool result = App.scheduler.cancel_retry(id(backoff_retry_sensor), STATIC_NAME);
            ESP_LOGI("test", "Static cancel result: %s", result ? "true" : "false");
          });

      # Wait for all tests to complete before reporting
      - delay: 500ms

      # Final report
      - logger.log: "=== Retry Test Results ==="
      - lambda: |-
          ESP_LOGI("test", "Simple retry counter: %d (expected 2)", id(simple_retry_counter));
          ESP_LOGI("test", "Backoff retry counter: %d (expected 4)", id(backoff_retry_counter));
          ESP_LOGI("test", "Immediate done counter: %d (expected 1)", id(immediate_done_counter));
          ESP_LOGI("test", "Cancel retry counter: %d (expected 2-4)", id(cancel_retry_counter));
          ESP_LOGI("test", "Empty name retry counter: %d (expected 1-2)", id(empty_name_retry_counter));
          ESP_LOGI("test", "Component retry counter: %d (expected 2)", id(script_retry_counter));
          ESP_LOGI("test", "Multiple same name counter: %d (expected 20+)", id(multiple_same_name_counter));
          ESP_LOGI("test", "Const char retry counter: %d (expected 1)", id(const_char_retry_counter));
          ESP_LOGI("test", "Static char retry counter: %d (expected 1)", id(static_char_retry_counter));
          ESP_LOGI("test", "All retry tests completed");
