<?php

declare(strict_types=1);

namespace Drupal\nodehive_path_alias\Authentication;

use Drupal\Core\Authentication\AuthenticationProviderInterface;
use Drupal\Core\Database\Connection;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
use Symfony\Component\HttpFoundation\Request;

/**
 * Authenticates decoupled_router requests using the NodeHive space API key.
 *
 * The nodehive_entity_access module blocks anonymous view access to nodes.
 * For the /router/translate-path endpoint, we need to authenticate the request
 * so that a valid space API key grants access to nodes within that space.
 *
 * This provider looks up the space by its apikey field (stored on the
 * nodehive_space entity), then returns the first editor of that space as
 * the authenticated user. Editors have view access to their space's nodes
 * via nodehive_entity_access.
 *
 * This is separate from nodehive_api_key_management which manages user-level
 * API keys (nhk_ prefix, stored in nodehive_api_keys table).
 */
final class RouterSpaceApiKeyProvider implements AuthenticationProviderInterface {

  public function __construct(
    private readonly Connection $database,
    private readonly EntityTypeManagerInterface $entityTypeManager,
    private readonly LoggerChannelFactoryInterface $loggerFactory,
  ) {}

  /**
   * {@inheritdoc}
   */
  public function applies(Request $request): bool {
    $path = $request->getPathInfo();
    $isRelevantPath = str_contains($path, '/router/translate-path')
      || str_contains($path, '/jsonapi/');

    if (!$isRelevantPath) {
      return FALSE;
    }

    $auth = $request->headers->get('Authorization', '');

    return str_starts_with(strtolower($auth), 'bearer ');
  }

  /**
   * {@inheritdoc}
   */
  public function authenticate(Request $request): ?object {
    $auth = $request->headers->get('Authorization', '');
    $token = trim(substr($auth, 7));
    if ($token === '') {
      return NULL;
    }

    $spaceId = $this->database->select('nodehive_space', 'ns')
      ->fields('ns', ['id'])
      ->condition('apikey', $token)
      ->range(0, 1)
      ->execute()
      ->fetchField();

    if ($spaceId === FALSE) {
      $this->loggerFactory->get('nodehive_path_alias')->warning(
        'Bearer token does not match any space API key for path "@path". Authentication rejected.',
        ['@path' => $request->getPathInfo()],
      );

      return NULL;
    }

    $space = $this->entityTypeManager
      ->getStorage('nodehive_space')
      ->load($spaceId);

    if (!$space instanceof FieldableEntityInterface) {
      $this->loggerFactory->get('nodehive_path_alias')->warning(
        'Space @space_id matched by API key could not be loaded as a fieldable entity.',
        ['@space_id' => $spaceId],
      );

      return NULL;
    }

    if (!$space->hasField('editors') || $space->get('editors')->isEmpty()) {
      $this->loggerFactory->get('nodehive_path_alias')->warning(
        'Space @space_id has no editors — cannot authenticate request. Add at least one editor to the space.',
        ['@space_id' => $spaceId],
      );

      return NULL;
    }

    $uid = $space->get('editors')->target_id;
    if (!$uid) {
      return NULL;
    }

    return $this->entityTypeManager->getStorage('user')->load($uid);
  }

}
