<?php

namespace Drupal\eb\Access;

use Drupal\Core\Access\AccessResult;
use Drupal\Core\Access\AccessResultInterface;
use Drupal\Core\Entity\EntityAccessControlHandler;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\eb\Entity\EbDefinition;

/**
 * Access control handler for EbDefinition entities.
 *
 * Implements ownership-based access control for entity builder definitions.
 * Supports both "own" and "any" permission patterns for CRUD operations.
 */
class EbDefinitionAccessControlHandler extends EntityAccessControlHandler {

  /**
   * {@inheritdoc}
   */
  protected function checkAccess(EntityInterface $entity, $operation, AccountInterface $account): AccessResultInterface {
    assert($entity instanceof EbDefinition);

    // Admin bypass - full access for administrators.
    if ($account->hasPermission('administer entity builder')) {
      return AccessResult::allowed()->cachePerPermissions();
    }

    $isOwner = $entity->getOwnerId() === (int) $account->id();

    return match ($operation) {
      'view' => $this->checkViewAccess($entity, $account, $isOwner),
      'update' => $this->checkUpdateAccess($entity, $account, $isOwner),
      'delete' => $this->checkDeleteAccess($entity, $account, $isOwner),
      'export' => $this->checkExportAccess($entity, $account, $isOwner),
      'apply' => $this->checkApplyAccess($entity, $account),
      'preview' => $this->checkPreviewAccess($entity, $account, $isOwner),
      default => AccessResult::neutral(),
    };
  }

  /**
   * {@inheritdoc}
   */
  protected function checkCreateAccess(AccountInterface $account, array $context, $entity_bundle = NULL): AccessResultInterface {
    return AccessResult::allowedIfHasPermissions($account, [
      'administer entity builder',
      'create entity definitions',
    ], 'OR')->cachePerPermissions();
  }

  /**
   * Checks view access for a definition.
   *
   * @param \Drupal\eb\Entity\EbDefinition $entity
   *   The definition entity.
   * @param \Drupal\Core\Session\AccountInterface $account
   *   The user account.
   * @param bool $isOwner
   *   Whether the user is the owner of the definition.
   *
   * @return \Drupal\Core\Access\AccessResultInterface
   *   The access result.
   */
  protected function checkViewAccess(EbDefinition $entity, AccountInterface $account, bool $isOwner): AccessResultInterface {
    if ($isOwner && $account->hasPermission('view own entity definitions')) {
      return AccessResult::allowed()
        ->cachePerUser()
        ->cachePerPermissions()
        ->addCacheableDependency($entity);
    }

    return AccessResult::forbidden()
      ->cachePerUser()
      ->cachePerPermissions()
      ->addCacheableDependency($entity);
  }

  /**
   * Checks update access for a definition.
   *
   * @param \Drupal\eb\Entity\EbDefinition $entity
   *   The definition entity.
   * @param \Drupal\Core\Session\AccountInterface $account
   *   The user account.
   * @param bool $isOwner
   *   Whether the user is the owner of the definition.
   *
   * @return \Drupal\Core\Access\AccessResultInterface
   *   The access result.
   */
  protected function checkUpdateAccess(EbDefinition $entity, AccountInterface $account, bool $isOwner): AccessResultInterface {
    // Check status restrictions - cannot edit definitions pending review.
    $status = $entity->getApplicationStatus();
    if (in_array($status, ['pending_review', 'in_review', 'approved'], TRUE)) {
      return AccessResult::forbidden('Definition is locked for review.')
        ->cachePerUser()
        ->addCacheableDependency($entity);
    }

    if ($isOwner && $account->hasPermission('edit own entity definitions')) {
      return AccessResult::allowed()
        ->cachePerUser()
        ->cachePerPermissions()
        ->addCacheableDependency($entity);
    }

    return AccessResult::forbidden()
      ->cachePerUser()
      ->cachePerPermissions()
      ->addCacheableDependency($entity);
  }

  /**
   * Checks delete access for a definition.
   *
   * @param \Drupal\eb\Entity\EbDefinition $entity
   *   The definition entity.
   * @param \Drupal\Core\Session\AccountInterface $account
   *   The user account.
   * @param bool $isOwner
   *   Whether the user is the owner of the definition.
   *
   * @return \Drupal\Core\Access\AccessResultInterface
   *   The access result.
   */
  protected function checkDeleteAccess(EbDefinition $entity, AccountInterface $account, bool $isOwner): AccessResultInterface {
    if ($isOwner && $account->hasPermission('delete own entity definitions')) {
      return AccessResult::allowed()
        ->cachePerUser()
        ->cachePerPermissions()
        ->addCacheableDependency($entity);
    }

    return AccessResult::forbidden()
      ->cachePerUser()
      ->cachePerPermissions()
      ->addCacheableDependency($entity);
  }

  /**
   * Checks export access for a definition.
   *
   * Export access is granted via two permission tiers:
   * - Tier 2: 'export entity architecture' grants full export access.
   * - Tier 1: 'export entity definitions' grants export of own definitions.
   *
   * @param \Drupal\eb\Entity\EbDefinition $entity
   *   The definition entity.
   * @param \Drupal\Core\Session\AccountInterface $account
   *   The user account.
   * @param bool $isOwner
   *   Whether the user is the owner of the definition.
   *
   * @return \Drupal\Core\Access\AccessResultInterface
   *   The access result.
   */
  protected function checkExportAccess(EbDefinition $entity, AccountInterface $account, bool $isOwner): AccessResultInterface {
    // Tier 2: Full export access for site architecture.
    if ($account->hasPermission('export entity architecture')) {
      return AccessResult::allowed()
        ->cachePerPermissions()
        ->addCacheableDependency($entity);
    }

    // Tier 1: Owner can export their own definitions.
    if ($isOwner && $account->hasPermission('export entity definitions')) {
      return AccessResult::allowed()
        ->cachePerUser()
        ->cachePerPermissions()
        ->addCacheableDependency($entity);
    }

    return AccessResult::forbidden()
      ->cachePerUser()
      ->cachePerPermissions()
      ->addCacheableDependency($entity);
  }

  /**
   * Checks apply access for a definition.
   *
   * Apply access is granted via two Tier 2 permissions:
   * - 'apply entity definitions' applies definitions created in the UI.
   * - 'import entity architecture' includes apply (imports AND applies).
   *
   * @param \Drupal\eb\Entity\EbDefinition $entity
   *   The definition entity.
   * @param \Drupal\Core\Session\AccountInterface $account
   *   The user account.
   *
   * @return \Drupal\Core\Access\AccessResultInterface
   *   The access result.
   */
  protected function checkApplyAccess(EbDefinition $entity, AccountInterface $account): AccessResultInterface {
    // Tier 2: Apply definitions created in the UI.
    if ($account->hasPermission('apply entity definitions')) {
      return AccessResult::allowed()
        ->cachePerPermissions()
        ->addCacheableDependency($entity);
    }

    // Tier 2: Import also grants apply (importing includes applying).
    if ($account->hasPermission('import entity architecture')) {
      return AccessResult::allowed()
        ->cachePerPermissions()
        ->addCacheableDependency($entity);
    }

    return AccessResult::forbidden()
      ->cachePerPermissions()
      ->addCacheableDependency($entity);
  }

  /**
   * Checks preview access for a definition.
   *
   * @param \Drupal\eb\Entity\EbDefinition $entity
   *   The definition entity.
   * @param \Drupal\Core\Session\AccountInterface $account
   *   The user account.
   * @param bool $isOwner
   *   Whether the user is the owner of the definition.
   *
   * @return \Drupal\Core\Access\AccessResultInterface
   *   The access result.
   */
  protected function checkPreviewAccess(EbDefinition $entity, AccountInterface $account, bool $isOwner): AccessResultInterface {
    // Preview permission is not ownership-based - anyone with permission can.
    if ($account->hasPermission('preview entity definitions')) {
      return AccessResult::allowed()
        ->cachePerPermissions()
        ->addCacheableDependency($entity);
    }

    return AccessResult::forbidden()
      ->cachePerPermissions()
      ->addCacheableDependency($entity);
  }

}
