<?php
/**
 * Authentication Exception
 *
 * @package Licenzo\Framework\Exceptions
 */

namespace Licenzo\Framework\Exceptions;

use Exception;

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

/**
 * Authentication Exception for handling authentication errors
 */
class AuthenticationException extends Exception
{
    /**
     * Authentication method that failed
     *
     * @var string|null
     */
    protected $method;

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

    /**
     * Constructor
     *
     * @param string $message Error message
     * @param string|null $method Authentication method
     * @param array $context Additional context
     * @param Exception|null $previous Previous exception
     */
    public function __construct(
        string $message = 'Authentication required',
        string $method = null,
        array $context = [],
        Exception $previous = null
    ) {
        $this->method = $method;
        $this->context = $context;

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

    /**
     * Get authentication method
     *
     * @return string|null
     */
    public function getMethod(): ?string
    {
        return $this->method;
    }

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

    /**
     * Set authentication method
     *
     * @param string $method
     * @return $this
     */
    public function setMethod(string $method): self
    {
        $this->method = $method;
        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 credentials
     *
     * @param string|null $method
     * @return static
     */
    public static function missingCredentials(string $method = null): self
    {
        return new static(
            'Authentication credentials are required',
            $method,
            ['reason' => 'missing_credentials']
        );
    }

    /**
     * Create exception for invalid credentials
     *
     * @param string|null $method
     * @return static
     */
    public static function invalidCredentials(string $method = null): self
    {
        return new static(
            'Invalid authentication credentials',
            $method,
            ['reason' => 'invalid_credentials']
        );
    }

    /**
     * Create exception for expired credentials
     *
     * @param string|null $method
     * @return static
     */
    public static function expiredCredentials(string $method = null): self
    {
        return new static(
            'Authentication credentials have expired',
            $method,
            ['reason' => 'expired_credentials']
        );
    }

    /**
     * Create exception for invalid token
     *
     * @return static
     */
    public static function invalidToken(): self
    {
        return new static(
            'Invalid authentication token',
            'token',
            ['reason' => 'invalid_token']
        );
    }

    /**
     * Create exception for expired token
     *
     * @return static
     */
    public static function expiredToken(): self
    {
        return new static(
            'Authentication token has expired',
            'token',
            ['reason' => 'expired_token']
        );
    }

    /**
     * Create exception for missing token
     *
     * @return static
     */
    public static function missingToken(): self
    {
        return new static(
            'Authentication token is required',
            'token',
            ['reason' => 'missing_token']
        );
    }

    /**
     * Create exception for invalid nonce
     *
     * @return static
     */
    public static function invalidNonce(): self
    {
        return new static(
            'Invalid security nonce',
            'nonce',
            ['reason' => 'invalid_nonce']
        );
    }

    /**
     * Create exception for missing nonce
     *
     * @return static
     */
    public static function missingNonce(): self
    {
        return new static(
            'Security nonce is required',
            'nonce',
            ['reason' => 'missing_nonce']
        );
    }

    /**
     * Create exception for WordPress user authentication
     *
     * @return static
     */
    public static function wordpressAuthRequired(): self
    {
        return new static(
            'WordPress user authentication required',
            'wordpress',
            ['reason' => 'wordpress_auth_required']
        );
    }

    /**
     * Create exception for insufficient capabilities
     *
     * @param string $capability
     * @return static
     */
    public static function insufficientCapabilities(string $capability): self
    {
        return new static(
            "User lacks required capability: {$capability}",
            'wordpress',
            [
                'reason' => 'insufficient_capabilities',
                'required_capability' => $capability
            ]
        );
    }

    /**
     * Create exception for API key authentication
     *
     * @param string $reason
     * @return static
     */
    public static function apiKeyAuth(string $reason = 'invalid'): self
    {
        $messages = [
            'missing' => 'API key is required',
            'invalid' => 'Invalid API key',
            'expired' => 'API key has expired',
            'revoked' => 'API key has been revoked',
        ];

        return new static(
            $messages[$reason] ?? 'API key authentication failed',
            'api_key',
            ['reason' => $reason]
        );
    }

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