<?php

declare(strict_types=1);

namespace Drupal\native_observability_cache_observer\Service;

use Drupal\Core\Cache\CacheTagsInvalidatorInterface;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\native_observability\EventSubscriber\TraceSubscriber;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\HttpFoundation\RequestStack;

/**
 * Observes cache tag invalidation activity without altering Drupal behavior.
 */
final class ObservedCacheTagsInvalidator implements CacheTagsInvalidatorInterface {

  /**
   * Creates a new observed cache tags invalidator instance.
   */
  public function __construct(
    private readonly CacheObserverStorage $storage,
    private readonly RequestStack $requestStack,
    #[Autowire(service: 'config.factory')]
    private readonly ConfigFactoryInterface $configFactory,
    #[Autowire(service: 'logger.channel.native_observability')]
    private readonly LoggerInterface $logger,
    private readonly CacheObserverEnvironmentSnapshotManager $cacheObserverEnvironmentSnapshotManager,
  ) {
  }

  /**
   * Invalidates observed cache tags and stores the event.
   */
  public function invalidateTags(array $tags): void {
    if (!$this->shouldCaptureInvalidations()) {
      return;
    }

    try {
      $request = $this->requestStack->getCurrentRequest();

      $normalizedTags = array_values(array_unique(array_filter(array_map(
        static fn(mixed $tag): string => trim((string) $tag),
        $tags,
      ))));

      $this->storage->insert([
        'request_id' => $request?->attributes->get(TraceSubscriber::ATTR_REQUEST_ID),
        'event_type' => 'tag_invalidation',
        'cache_layer' => 'cache_tags',
        'status' => 'INVALIDATED',
        'method' => $request?->getMethod(),
        'path' => $request?->getPathInfo(),
        'route_name' => $request?->attributes->get('_route'),
        'tag_count' => count($normalizedTags),
        'context_count' => 0,
        'max_age' => NULL,
        'payload' => [
          'tags' => $normalizedTags,
          'runtime_environment_snapshot' => $this->cacheObserverEnvironmentSnapshotManager->getSnapshot(),
        ],
      ]);
    }
    catch (\Throwable $throwable) {
      $this->logger->warning('Cache observer could not record cache tag invalidation activity: @message', [
        '@message' => $throwable->getMessage(),
      ]);
    }
  }

  /**
   * Determines whether cache tag invalidation capture is enabled.
   *
   * @return bool
   *   TRUE when invalidation capture is enabled for the observer.
   */
  private function shouldCaptureInvalidations(): bool {
    $config = $this->configFactory->get('native_observability_cache_observer.settings');

    return (bool) $config->get('capture_enabled')
      && (bool) $config->get('capture_tag_invalidations');
  }

}
