<?php

declare(strict_types=1);

namespace App\Framework\LiveComponents\Exceptions;

use App\Framework\Exception\ErrorCode;
use App\Framework\Exception\FrameworkException;
use App\Framework\LiveComponents\Attributes\RequiresPermission;

/**
 * Exception thrown when user lacks permission for LiveComponent action
 *
 * Framework Integration:
 * - Extends FrameworkException for consistent error handling
 * - Includes rich context for debugging and logging
 * - Provides user-friendly error messages
 * - Respects Framework's security event logging
 */
final class UnauthorizedActionException extends FrameworkException
{
    /**
     * Create exception for missing permission
     *
     * @param string $componentName Component name
     * @param string $actionName Action method name
     * @param RequiresPermission $requiredPermission Required permission attribute
     * @param array<string> $userPermissions User's actual permissions
     */
    public static function forMissingPermission(
        string $componentName,
        string $actionName,
        RequiresPermission $requiredPermission,
        array $userPermissions
    ): self {
        $requiredPerms = $requiredPermission->getPermissions();
        $primaryPerm = $requiredPermission->getPrimaryPermission();

        $message = $requiredPermission->hasMultiplePermissions()
            ? "Action '{$actionName}' on component '{$componentName}' requires one of: " . implode(', ', $requiredPerms)
            : "Action '{$actionName}' on component '{$componentName}' requires permission: {$primaryPerm}";

        return self::create(ErrorCode::AUTH_INSUFFICIENT_PRIVILEGES, $message)
            ->withData([
                'component' => $componentName,
                'action' => $actionName,
                'required_permissions' => $requiredPerms,
                'user_permissions' => $userPermissions,
                'missing_permissions' => array_diff($requiredPerms, $userPermissions),
            ])
            ->withMetadata([
                'security_event' => true,
                'log_level' => 'warning',
            ]);
    }

    /**
     * Create exception for unauthenticated user
     */
    public static function forUnauthenticatedUser(
        string $componentName,
        string $actionName
    ): self {
        return self::create(
            ErrorCode::AUTH_UNAUTHORIZED,
            "Action '{$actionName}' on component '{$componentName}' requires authentication"
        )->withData([
            'component' => $componentName,
            'action' => $actionName,
            'reason' => 'User not authenticated',
        ])->withMetadata([
            'security_event' => true,
            'log_level' => 'info',
        ]);
    }

    /**
     * Create exception for generic authorization failure
     */
    public static function forAction(
        string $componentName,
        string $actionName,
        string $reason = 'Authorization check failed'
    ): self {
        return self::create(
            ErrorCode::AUTH_UNAUTHORIZED,
            "Not authorized to execute action '{$actionName}' on component '{$componentName}'"
        )->withData([
            'component' => $componentName,
            'action' => $actionName,
            'reason' => $reason,
        ])->withMetadata([
            'security_event' => true,
            'log_level' => 'warning',
        ]);
    }

    /**
     * Get user-friendly error message (safe for client-side display)
     */
    public function getUserMessage(): string
    {
        return match ($this->getErrorCode()) {
            ErrorCode::AUTH_UNAUTHORIZED => 'Please log in to perform this action',
            ErrorCode::AUTH_INSUFFICIENT_PRIVILEGES => 'You do not have permission to perform this action',
            default => 'Authorization failed'
        };
    }

    /**
     * Check if this is an authentication issue (vs authorization)
     */
    public function isAuthenticationIssue(): bool
    {
        $data = $this->getData();

        return isset($data['reason']) && $data['reason'] === 'User not authenticated';
    }

    /**
     * Get missing permissions (for debugging)
     *
     * @return array<string>
     */
    public function getMissingPermissions(): array
    {
        $data = $this->getData();

        return $data['missing_permissions'] ?? [];
    }
}
