<?php

declare(strict_types=1);

namespace Drupal\flowdrop_session\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\flowdrop\Constants\Permissions as FlowDropPermissions;
use Drupal\flowdrop_session\Constants\Permissions;
use Drupal\flowdrop_session\Entity\FlowDropSessionInterface;

/**
 * Access controller for the Session entity.
 *
 * Handles permission checks for viewing, editing, and deleting sessions
 * with support for ownership-based access control.
 *
 * @internal This class is not part of the public API and may change without notice.
 */
class FlowDropSessionAccessControlHandler extends EntityAccessControlHandler {

  /**
   * {@inheritdoc}
   */
  protected function checkAccess(EntityInterface $entity, mixed $operation, AccountInterface $account): AccessResultInterface {
    $operation = (string) $operation;
    if (!$entity instanceof FlowDropSessionInterface) {
      return AccessResult::neutral();
    }

    // Global super-admin bypass.
    if ($account->hasPermission(FlowDropPermissions::ADMINISTER_FLOWDROP)) {
      return AccessResult::allowed()->cachePerPermissions();
    }

    // Entity type admin bypass.
    if ($account->hasPermission(Permissions::ADMINISTER)) {
      return AccessResult::allowed()->cachePerPermissions();
    }

    switch ($operation) {
      case 'view':
        if ($account->hasPermission(Permissions::VIEW_ANY)) {
          return AccessResult::allowed()->cachePerPermissions();
        }
        if ($account->hasPermission(Permissions::VIEW_OWN)) {
          // Cast both sides: entity keys come back from storage as
          // strings (the MySQL driver stringifies fetches), so a strict
          // comparison without the cast denies every owner.
          $owner_id = (int) $entity->getOwnerId();
          $account_id = (int) $account->id();
          $is_owner = $owner_id === $account_id;
          return AccessResult::allowedIf($is_owner)
            ->cachePerPermissions()
            ->cachePerUser()
            ->addCacheableDependency($entity);
        }
        return AccessResult::forbidden()->cachePerPermissions();

      case 'update':
        if ($account->hasPermission(Permissions::EDIT_ANY)) {
          return AccessResult::allowed()->cachePerPermissions();
        }
        if ($account->hasPermission(Permissions::EDIT_OWN)) {
          // Cast both sides: entity keys come back from storage as
          // strings (the MySQL driver stringifies fetches), so a strict
          // comparison without the cast denies every owner.
          $owner_id = (int) $entity->getOwnerId();
          $account_id = (int) $account->id();
          $is_owner = $owner_id === $account_id;
          return AccessResult::allowedIf($is_owner)
            ->cachePerPermissions()
            ->cachePerUser()
            ->addCacheableDependency($entity);
        }
        return AccessResult::forbidden()->cachePerPermissions();

      case 'delete':
        if ($account->hasPermission(Permissions::DELETE_ANY)) {
          return AccessResult::allowed()->cachePerPermissions();
        }
        if ($account->hasPermission(Permissions::DELETE_OWN)) {
          // Cast both sides: entity keys come back from storage as
          // strings (the MySQL driver stringifies fetches), so a strict
          // comparison without the cast denies every owner.
          $owner_id = (int) $entity->getOwnerId();
          $account_id = (int) $account->id();
          $is_owner = $owner_id === $account_id;
          return AccessResult::allowedIf($is_owner)
            ->cachePerPermissions()
            ->cachePerUser()
            ->addCacheableDependency($entity);
        }
        return AccessResult::forbidden()->cachePerPermissions();

      default:
        return AccessResult::neutral();
    }
  }

  /**
   * {@inheritdoc}
   */
  protected function checkCreateAccess(AccountInterface $account, array $context, mixed $entity_bundle = NULL): AccessResultInterface {
    if ($account->hasPermission(FlowDropPermissions::ADMINISTER_FLOWDROP)) {
      return AccessResult::allowed()->cachePerPermissions();
    }

    if ($account->hasPermission(Permissions::ADMINISTER)) {
      return AccessResult::allowed()->cachePerPermissions();
    }

    if ($account->hasPermission(Permissions::CREATE)) {
      return AccessResult::allowed()->cachePerPermissions();
    }

    return AccessResult::forbidden()->cachePerPermissions();
  }

}
