<?php

namespace MintyPHP\Service\Access;

use MintyPHP\Service\Tenant\TenantScopeService;

class DepartmentAuthorizationPolicy implements AuthorizationPolicyInterface
{
    use AuthorizationPolicyContextTrait;

    public const ABILITY_ADMIN_DEPARTMENTS_VIEW = 'admin.departments.view';
    public const ABILITY_ADMIN_DEPARTMENTS_CREATE = 'admin.departments.create';
    public const ABILITY_ADMIN_DEPARTMENTS_EDIT_CONTEXT = 'admin.departments.edit.context';
    public const ABILITY_ADMIN_DEPARTMENTS_EDIT_SUBMIT = 'admin.departments.edit.submit';
    public const ABILITY_ADMIN_DEPARTMENTS_DELETE = 'admin.departments.delete';

    public function __construct(
        private readonly PermissionService $permissionService,
        private readonly TenantScopeService $scopeGateway
    ) {
    }

    public function supports(string $ability): bool
    {
        return in_array($ability, [
            self::ABILITY_ADMIN_DEPARTMENTS_VIEW,
            self::ABILITY_ADMIN_DEPARTMENTS_CREATE,
            self::ABILITY_ADMIN_DEPARTMENTS_EDIT_CONTEXT,
            self::ABILITY_ADMIN_DEPARTMENTS_EDIT_SUBMIT,
            self::ABILITY_ADMIN_DEPARTMENTS_DELETE,
        ], true);
    }

    public function authorize(string $ability, array $context = []): AuthorizationDecision
    {
        return match ($ability) {
            self::ABILITY_ADMIN_DEPARTMENTS_VIEW => $this->authorizeAdminDepartmentsView($context),
            self::ABILITY_ADMIN_DEPARTMENTS_CREATE => $this->authorizeAdminDepartmentsCreate($context),
            self::ABILITY_ADMIN_DEPARTMENTS_EDIT_CONTEXT => $this->authorizeAdminDepartmentsEditContext($context),
            self::ABILITY_ADMIN_DEPARTMENTS_EDIT_SUBMIT => $this->authorizeAdminDepartmentsEditSubmit($context),
            self::ABILITY_ADMIN_DEPARTMENTS_DELETE => $this->authorizeAdminDepartmentsDelete($context),
            default => AuthorizationDecision::deny(500, 'authorization_ability_not_supported'),
        };
    }

    private function authorizeAdminDepartmentsView(array $context): AuthorizationDecision
    {
        $actorUserId = $this->actorUserId($context);
        if (!$this->hasPermission($actorUserId, PermissionService::DEPARTMENTS_VIEW)) {
            return AuthorizationDecision::deny(403, 'forbidden');
        }

        $allowedTenantIds = $this->scopeGateway->getUserTenantIds($actorUserId);
        $isStrictScope = $this->scopeGateway->isStrict();

        return AuthorizationDecision::allow([
            'capabilities' => [
                'can_view_page' => true,
                'can_create_department' => $this->hasPermission($actorUserId, PermissionService::DEPARTMENTS_CREATE),
                'allowed_tenant_ids' => $allowedTenantIds,
                'is_strict_scope' => $isStrictScope,
            ],
        ]);
    }

    private function authorizeAdminDepartmentsCreate(array $context): AuthorizationDecision
    {
        $actorUserId = $this->actorUserId($context);
        if (!$this->hasPermission($actorUserId, PermissionService::DEPARTMENTS_CREATE)) {
            return AuthorizationDecision::deny(403, 'forbidden');
        }

        $allowedTenantIds = $this->scopeGateway->getUserTenantIds($actorUserId);
        $isStrictScope = $this->scopeGateway->isStrict();
        if ($isStrictScope && !$allowedTenantIds) {
            return AuthorizationDecision::deny(403, 'permission_denied');
        }

        return AuthorizationDecision::allow([
            'capabilities' => [
                'can_view_page' => true,
                'allowed_tenant_ids' => $allowedTenantIds,
                'is_strict_scope' => $isStrictScope,
            ],
        ]);
    }

    private function authorizeAdminDepartmentsEditContext(array $context): AuthorizationDecision
    {
        $actorUserId = $this->actorUserId($context);
        $targetDepartmentId = $this->targetDepartmentId($context);
        if ($actorUserId <= 0 || $targetDepartmentId <= 0) {
            return AuthorizationDecision::deny(403, 'forbidden');
        }
        if (!$this->hasPermission($actorUserId, PermissionService::DEPARTMENTS_VIEW)) {
            return AuthorizationDecision::deny(403, 'forbidden');
        }
        if (!$this->scopeGateway->canAccess('departments', $targetDepartmentId, $actorUserId)) {
            return AuthorizationDecision::deny(403, 'permission_denied');
        }

        return AuthorizationDecision::allow([
            'capabilities' => [
                'can_view_page' => true,
                'can_update_department' => $this->hasPermission($actorUserId, PermissionService::DEPARTMENTS_UPDATE),
                'can_delete_department' => $this->hasPermission($actorUserId, PermissionService::DEPARTMENTS_DELETE),
                'allowed_tenant_ids' => $this->scopeGateway->getUserTenantIds($actorUserId),
            ],
        ]);
    }

    private function authorizeAdminDepartmentsEditSubmit(array $context): AuthorizationDecision
    {
        $contextDecision = $this->authorizeAdminDepartmentsEditContext($context);
        if (!$contextDecision->isAllowed()) {
            return $contextDecision;
        }

        $capabilities = $this->capabilitiesFromDecision($contextDecision);
        if (!($capabilities['can_update_department'] ?? false)) {
            return AuthorizationDecision::deny(403, 'forbidden');
        }

        return AuthorizationDecision::allow(['capabilities' => $capabilities]);
    }

    private function authorizeAdminDepartmentsDelete(array $context): AuthorizationDecision
    {
        $actorUserId = $this->actorUserId($context);
        $targetDepartmentId = $this->targetDepartmentId($context);
        if ($actorUserId <= 0 || $targetDepartmentId <= 0) {
            return AuthorizationDecision::deny(403, 'forbidden');
        }
        if (!$this->hasPermission($actorUserId, PermissionService::DEPARTMENTS_DELETE)) {
            return AuthorizationDecision::deny(403, 'forbidden');
        }
        if (!$this->scopeGateway->canAccess('departments', $targetDepartmentId, $actorUserId)) {
            return AuthorizationDecision::deny(403, 'permission_denied');
        }

        return AuthorizationDecision::allow();
    }

    private function targetDepartmentId(array $context): int
    {
        return (int) ($context['target_department_id'] ?? 0);
    }
}
