<?php
/**
 * Authorization Exception
 *
 * @package ZenoChatbot\Framework\Exceptions
 */

namespace ZenoChatbot\Framework\Exceptions;

use Exception;

// Prevent direct access
if (!defined('ABSPATH')) {
    exit;
}

/**
 * Authorization Exception for handling authorization/permission errors
 */
class AuthorizationException extends Exception
{
    /**
     * Required permission/capability
     *
     * @var string|null
     */
    protected $permission;

    /**
     * Resource being accessed
     *
     * @var string|null
     */
    protected $resource;

    /**
     * Action being performed
     *
     * @var string|null
     */
    protected $action;

    /**
     * Additional context data
     *
     * @var array
     */
    protected $context = [];

    /**
     * Constructor
     *
     * @param string $message Error message
     * @param string|null $permission Required permission
     * @param string|null $resource Resource being accessed
     * @param string|null $action Action being performed
     * @param array $context Additional context
     * @param Exception|null $previous Previous exception
     */
    public function __construct(
        string $message = 'Access denied',
        string $permission = null,
        string $resource = null,
        string $action = null,
        array $context = [],
        Exception $previous = null
    ) {
        $this->permission = $permission;
        $this->resource = $resource;
        $this->action = $action;
        $this->context = $context;

        parent::__construct($message, 403, $previous);
    }

    /**
     * Get required permission
     *
     * @return string|null
     */
    public function getPermission(): ?string
    {
        return $this->permission;
    }

    /**
     * Get resource being accessed
     *
     * @return string|null
     */
    public function getResource(): ?string
    {
        return $this->resource;
    }

    /**
     * Get action being performed
     *
     * @return string|null
     */
    public function getAction(): ?string
    {
        return $this->action;
    }

    /**
     * Get context data
     *
     * @return array
     */
    public function getContext(): array
    {
        return $this->context;
    }

    /**
     * Set required permission
     *
     * @param string $permission
     * @return $this
     */
    public function setPermission(string $permission): self
    {
        $this->permission = $permission;
        return $this;
    }

    /**
     * Set resource being accessed
     *
     * @param string $resource
     * @return $this
     */
    public function setResource(string $resource): self
    {
        $this->resource = $resource;
        return $this;
    }

    /**
     * Set action being performed
     *
     * @param string $action
     * @return $this
     */
    public function setAction(string $action): self
    {
        $this->action = $action;
        return $this;
    }

    /**
     * Set context data
     *
     * @param array $context
     * @return $this
     */
    public function setContext(array $context): self
    {
        $this->context = $context;
        return $this;
    }

    /**
     * Add context item
     *
     * @param string $key
     * @param mixed $value
     * @return $this
     */
    public function addContext(string $key, $value): self
    {
        $this->context[$key] = $value;
        return $this;
    }

    /**
     * Create exception for missing capability
     *
     * @param string $capability
     * @param string|null $resource
     * @return static
     */
    public static function missingCapability(string $capability, string $resource = null): self
    {
        $message = "Access denied. Required capability: {$capability}";
        if ($resource) {
            $message .= " for resource: {$resource}";
        }

        return new static(
            $message,
            $capability,
            $resource,
            null,
            ['reason' => 'missing_capability']
        );
    }

    /**
     * Create exception for insufficient role
     *
     * @param string $requiredRole
     * @param string|null $currentRole
     * @return static
     */
    public static function insufficientRole(string $requiredRole, string $currentRole = null): self
    {
        $message = "Access denied. Required role: {$requiredRole}";
        if ($currentRole) {
            $message .= ". Current role: {$currentRole}";
        }

        return new static(
            $message,
            $requiredRole,
            null,
            null,
            [
                'reason' => 'insufficient_role',
                'required_role' => $requiredRole,
                'current_role' => $currentRole
            ]
        );
    }

    /**
     * Create exception for resource ownership
     *
     * @param string $resource
     * @param int|null $resourceId
     * @return static
     */
    public static function notOwner(string $resource, int $resourceId = null): self
    {
        $message = "Access denied. You don't own this {$resource}";
        if ($resourceId) {
            $message .= " (ID: {$resourceId})";
        }

        return new static(
            $message,
            'ownership',
            $resource,
            'access',
            [
                'reason' => 'not_owner',
                'resource_id' => $resourceId
            ]
        );
    }

    /**
     * Create exception for action not allowed
     *
     * @param string $action
     * @param string|null $resource
     * @return static
     */
    public static function actionNotAllowed(string $action, string $resource = null): self
    {
        $message = "Action '{$action}' is not allowed";
        if ($resource) {
            $message .= " on resource '{$resource}'";
        }

        return new static(
            $message,
            null,
            $resource,
            $action,
            ['reason' => 'action_not_allowed']
        );
    }

    /**
     * Create exception for admin only access
     *
     * @param string|null $resource
     * @return static
     */
    public static function adminOnly(string $resource = null): self
    {
        $message = 'Access denied. Administrator privileges required';
        if ($resource) {
            $message .= " to access {$resource}";
        }

        return new static(
            $message,
            'manage_options',
            $resource,
            null,
            ['reason' => 'admin_only']
        );
    }

    /**
     * Create exception for rate limiting
     *
     * @param int $limit
     * @param string $period
     * @return static
     */
    public static function rateLimited(int $limit, string $period): self
    {
        return new static(
            "Rate limit exceeded. Maximum {$limit} requests per {$period}",
            null,
            null,
            null,
            [
                'reason' => 'rate_limited',
                'limit' => $limit,
                'period' => $period
            ]
        );
    }

    /**
     * Create exception for IP blocking
     *
     * @param string|null $ip
     * @return static
     */
    public static function ipBlocked(string $ip = null): self
    {
        $message = 'Access denied. Your IP address has been blocked';
        if ($ip) {
            $message .= " ({$ip})";
        }

        return new static(
            $message,
            null,
            null,
            null,
            [
                'reason' => 'ip_blocked',
                'ip' => $ip
            ]
        );
    }

    /**
     * Create exception for maintenance mode
     *
     * @return static
     */
    public static function maintenanceMode(): self
    {
        return new static(
            'Service temporarily unavailable due to maintenance',
            null,
            null,
            null,
            ['reason' => 'maintenance_mode']
        );
    }

    /**
     * Convert to array for API response
     *
     * @return array
     */
    public function toArray(): array
    {
        return [
            'message' => $this->getMessage(),
            'permission' => $this->getPermission(),
            'resource' => $this->getResource(),
            'action' => $this->getAction(),
            'context' => $this->getContext(),
        ];
    }
}
