<?php

declare(strict_types=1);

namespace Drupal\simpleavs\Hook;

use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Path\CurrentPathStack;
use Drupal\Core\Path\PathMatcherInterface;
use Drupal\path_alias\AliasManagerInterface;
use Drupal\Core\PageCache\ResponsePolicy\KillSwitch;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Url;

/**
 * OO hook implementations for SimpleAVS.
 */
final class SimpleAvsHooks {

  public function __construct(
    private readonly ConfigFactoryInterface $configFactory,
    private readonly PathMatcherInterface $pathMatcher,
    private readonly AliasManagerInterface $aliasManager,
    private readonly CurrentPathStack $currentPath,
    private readonly KillSwitch $killSwitch,
  ) {}

  /**
   * Hook: hook_page_attachments().
   *
   * @param array $attachments
   *   The page attachments render array (passed by reference).
   */
  public function pageAttachments(array &$attachments): void {
    $cfg = $this->configFactory->get('simpleavs.settings');

    // Bubble cache metadata so config changes invalidate rendered pages.
    $cm = new CacheableMetadata();
    $cm->addCacheTags(['config:simpleavs.settings'])
      ->addCacheContexts(['url.path', 'url.path.is_front', 'route'])
      ->applyTo($attachments);

    $enabled = (bool) ($cfg->get('enabled') ?? FALSE);
    $method = (string) ($cfg->get('method') ?? 'question');
    $min_age = (int) ($cfg->get('min_age') ?? 18);
    $frequency = (string) ($cfg->get('frequency') ?? 'never');

    $path_mode = (string) ($cfg->get('path_mode') ?? 'exclude');
    $path_patterns = (string) ($cfg->get('path_patterns') ?? '');

    $redirect_success = (string) ($cfg->get('redirect_success') ?? '');
    $redirect_failure = (string) ($cfg->get('redirect_failure') ?? '');

    $strings = $cfg->get('strings') ?? [];
    $appearance = $cfg->get('appearance') ?? [];

    $date_format = (string) ($cfg->get('date_format') ?? 'mdy');
    $date_format = ($date_format === 'dmy') ? 'dmy' : 'mdy';

    // Only attach when the gate should show on this path.
    if ($enabled && $this->isRequiredRoute($path_mode, $path_patterns)) {
      // Disable Drupal page cache for this request so changes are immediate.
      $this->killSwitch->trigger();

      // Also set max-age 0 on this attachment to discourage proxy caching.
      $attachments['#cache']['max-age'] = 0;

      $attachments['#attached']['library'][] = 'simpleavs/agegate';
      $attachments['#attached']['drupalSettings']['simpleavs'] = [
        'enabled' => TRUE,
        'method' => $method,
        'min_age' => $min_age,
        'frequency' => $frequency,
        'paths' => ['mode' => $path_mode, 'patterns' => $path_patterns],
        'redirects' => [
          'success' => $redirect_success,
          'failure' => $redirect_failure,
        ],
        'strings' => $strings,
        'appearance' => $appearance,
        'endpoints' => [
          'token' => Url::fromRoute('simpleavs.token')->setAbsolute()->toString(),
          'verify' => Url::fromRoute('simpleavs.verify')->setAbsolute()->toString(),
        ],
        'date_format' => $date_format, // 'mdy' | 'dmy'
      ];
    }
  }

  /**
   * Lightweight path matcher mirroring the module's scoping rules.
   *
   * @param string $mode
   *   Either 'include' or 'exclude'.
   * @param string $patterns
   *   Path patterns, one per line, <front> supported, wildcards allowed.
   *
   * @return bool
   *   TRUE if the age gate should apply on the current route.
   */
  private function isRequiredRoute(string $mode, string $patterns): bool {
    $cur = $this->currentPath->getPath();
    $alias = $this->aliasManager->getAliasByPath($cur);

    $raw = (string) $patterns;
    $norm = str_replace(["\r\n", "\r", ","], "\n", $raw);
    $lines = array_values(array_filter(
      array_map('trim', explode("\n", $norm)),
      static fn($v) => $v !== ''
    ));

    $hasFront = FALSE;
    foreach ($lines as $i => $p) {
      if (strcasecmp($p, '<front>') === 0) {
        $hasFront = TRUE;
        unset($lines[$i]);
      }
    }
    $blob = implode("\n", $lines);

    $isFront = $this->pathMatcher->isFrontPage();
    $match_current = $blob !== '' ? $this->pathMatcher->matchPath($cur, $blob) : FALSE;
    $match_alias = ($blob !== '' && $alias !== $cur)
      ? $this->pathMatcher->matchPath($alias, $blob)
      : FALSE;
    $listed = $match_current || $match_alias || ($hasFront && $isFront);

    return $mode === 'include' ? $listed : !$listed;
  }

}
