<?php

declare(strict_types=1);

namespace Drupal\native_observability\Service;

use Drupal\Core\Config\ConfigFactoryInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\DependencyInjection\Attribute\TaggedIterator;

/**
 * Coordinates observability data cleanup across registered handlers.
 */
final class ObservabilityCleanupManager {

  /**
   * Constructs the cleanup manager.
   *
   * @param iterable<\Drupal\native_observability\Service\ObservabilityCleanupHandlerInterface> $handlers
   *   Tagged cleanup handlers.
   * @param \Drupal\Core\Config\ConfigFactoryInterface $configFactory
   *   Config factory.
   * @param \Psr\Log\LoggerInterface $logger
   *   Dedicated logger channel.
   */
  public function __construct(
    #[TaggedIterator('native_observability.cleanup_handler')]
    private readonly iterable $handlers,
    private readonly ConfigFactoryInterface $configFactory,
    #[Autowire(service: 'logger.channel.native_observability')]
    private readonly LoggerInterface $logger,
  ) {
  }

  /**
   * Runs cleanup for cron using stored retention settings.
   *
   * Automatic cleanup is executed only when explicitly enabled.
   *
   * @return array
   *   Cleanup results keyed by handler id.
   */
  public function runCleanup(): array {
    $config = $this->configFactory->get('native_observability.settings');

    if (!$config->get('cleanup_enabled')) {
      return [];
    }

    return $this->runWithSettings($this->getStoredSettings());
  }

  /**
   * Runs manual cleanup immediately using stored retention settings.
   *
   * This operation does not depend on the automatic cleanup toggle.
   *
   * @return array
   *   Cleanup results keyed by handler id.
   */
  public function runManualCleanup(): array {
    return $this->runWithSettings($this->getStoredSettings());
  }

  /**
   * Returns stored cleanup settings.
   *
   * @return array
   *   Normalized cleanup settings.
   */
  private function getStoredSettings(): array {
    $config = $this->configFactory->get('native_observability.settings');

    return [
      'trace_retention_hours' => (int) ($config->get('trace_retention_hours') ?? 24),
      'spans_retention_hours' => (int) ($config->get('spans_retention_hours') ?? 24),
      'metrics_retention_days' => (int) ($config->get('metrics_retention_days') ?? 7),
      'cleanup_batch_limit' => (int) ($config->get('cleanup_batch_limit') ?? 5000),
    ];
  }

  /**
   * Runs cleanup for all registered handlers using explicit settings.
   *
   * @param array $settings
   *   Cleanup settings.
   *
   * @return array
   *   Cleanup results keyed by handler id.
   */
  public function runWithSettings(array $settings): array {
    $results = [];

    foreach ($this->handlers as $handler) {
      if (!$handler->isAvailable()) {
        $results[$handler->id()] = 0;
        continue;
      }

      $deleted = $handler->cleanup($settings);
      $results[$handler->id()] = $deleted;

      $this->logger->info(
        'Observability cleanup handler "@handler" deleted @count records.',
        [
          '@handler' => $handler->id(),
          '@count' => $deleted,
        ]
      );
    }

    return $results;
  }

  /**
   * Runs a forced full cleanup across all registered handlers.
   *
   * @return array
   *   Cleanup results keyed by handler id.
   */
  public function runForcedCleanupAll(): array {
    $results = [];

    foreach ($this->handlers as $handler) {
      if (!$handler->isAvailable()) {
        $results[$handler->id()] = 0;
        continue;
      }

      $deleted = $handler->cleanupAll();
      $results[$handler->id()] = $deleted;

      $this->logger->warning(
        'Observability forced cleanup handler "@handler" deleted @count records.',
        [
          '@handler' => $handler->id(),
          '@count' => $deleted,
        ]
      );
    }

    return $results;
  }

}
