<?php

declare(strict_types=1);

namespace App\Framework\Console\Security;

use App\Framework\Console\Security\Attributes\PermissionMode;
use App\Framework\Console\Security\Attributes\RequiresPermission;

final readonly class SecurityService
{
    public function __construct(
        private UserProvider $userProvider
    ) {
    }

    public function getCurrentUser(): ConsoleUser
    {
        return $this->userProvider->getCurrentUser();
    }

    public function checkPermission(Permission $permission): bool
    {
        $user = $this->getCurrentUser();

        return $user->hasPermission($permission);
    }

    public function checkPermissions(array $permissions, PermissionMode $mode = PermissionMode::ANY): bool
    {
        $user = $this->getCurrentUser();
        $userPermissions = array_map(fn (Permission $p) => $p->value, $user->permissions);
        $requiredPermissions = array_map(fn (Permission $p) => $p->value, $permissions);

        return $mode->check($userPermissions, $requiredPermissions);
    }

    public function checkRequiresPermissionAttribute(RequiresPermission $attribute): bool
    {
        return $this->checkPermissions($attribute->getPermissions(), $attribute->mode);
    }

    public function requirePermission(Permission $permission): void
    {
        if (! $this->checkPermission($permission)) {
            throw new SecurityException(
                "Access denied. Required permission: {$permission->value}",
                $permission,
                $this->getCurrentUser()
            );
        }
    }

    public function requirePermissions(array $permissions, PermissionMode $mode = PermissionMode::ANY): void
    {
        if (! $this->checkPermissions($permissions, $mode)) {
            $permissionNames = array_map(fn (Permission $p) => $p->value, $permissions);
            $modeText = $mode === PermissionMode::ALL ? 'all' : 'any';

            throw new SecurityException(
                "Access denied. Required {$modeText} of permissions: " . implode(', ', $permissionNames),
                $permissions,
                $this->getCurrentUser()
            );
        }
    }

    public function isAuthenticated(): bool
    {
        return $this->userProvider->isAuthenticated();
    }

    public function isAdmin(): bool
    {
        return $this->getCurrentUser()->isAdmin();
    }

    public function canAccessCategory(PermissionCategory $category): bool
    {
        $user = $this->getCurrentUser();
        $categoryPermissions = $category->getPermissions();

        return $user->hasAnyPermission($categoryPermissions);
    }

    public function getAccessibleCategories(): array
    {
        return array_filter(
            PermissionCategory::cases(),
            fn (PermissionCategory $category) => $this->canAccessCategory($category)
        );
    }

    public function getUserPermissionsByCategory(): array
    {
        $user = $this->getCurrentUser();
        $result = [];

        foreach (PermissionCategory::cases() as $category) {
            $permissions = $user->getPermissionsByCategory($category);
            if (! empty($permissions)) {
                $result[$category->value] = array_map(fn (Permission $p) => $p->value, $permissions);
            }
        }

        return $result;
    }
}
