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

namespace ZenoChatbot\Framework\Exceptions;

use Exception;

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

/**
 * Configuration Exception for handling configuration-related errors
 */
class ConfigurationException extends Exception
{
    /**
     * Configuration key that caused the error
     *
     * @var string|null
     */
    protected $configKey;

    /**
     * Configuration file that caused the error
     *
     * @var string|null
     */
    protected $configFile;

    /**
     * Expected value type or format
     *
     * @var string|null
     */
    protected $expectedType;

    /**
     * Actual value that caused the error
     *
     * @var mixed
     */
    protected $actualValue;

    /**
     * Constructor
     *
     * @param string $message Error message
     * @param string|null $configKey Configuration key
     * @param string|null $configFile Configuration file
     * @param string|null $expectedType Expected type
     * @param mixed $actualValue Actual value
     * @param Exception|null $previous Previous exception
     */
    public function __construct(
        string $message,
        string $configKey = null,
        string $configFile = null,
        string $expectedType = null,
        $actualValue = null,
        Exception $previous = null
    ) {
        $this->configKey = $configKey;
        $this->configFile = $configFile;
        $this->expectedType = $expectedType;
        $this->actualValue = $actualValue;

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

    /**
     * Get configuration key
     *
     * @return string|null
     */
    public function getConfigKey(): ?string
    {
        return $this->configKey;
    }

    /**
     * Get configuration file
     *
     * @return string|null
     */
    public function getConfigFile(): ?string
    {
        return $this->configFile;
    }

    /**
     * Get expected type
     *
     * @return string|null
     */
    public function getExpectedType(): ?string
    {
        return $this->expectedType;
    }

    /**
     * Get actual value
     *
     * @return mixed
     */
    public function getActualValue()
    {
        return $this->actualValue;
    }

    /**
     * Create exception for missing configuration
     *
     * @param string $key
     * @param string|null $file
     * @return static
     */
    public static function missing(string $key, string $file = null): self
    {
        $message = "Missing required configuration: {$key}";
        if ($file) {
            $message .= " in file: {$file}";
        }

        return new static($message, $key, $file);
    }

    /**
     * Create exception for invalid configuration type
     *
     * @param string $key
     * @param string $expectedType
     * @param mixed $actualValue
     * @param string|null $file
     * @return static
     */
    public static function invalidType(
        string $key,
        string $expectedType,
        $actualValue,
        string $file = null
    ): self {
        $actualType = gettype($actualValue);
        $message = "Invalid configuration type for '{$key}'. Expected {$expectedType}, got {$actualType}";
        
        if ($file) {
            $message .= " in file: {$file}";
        }

        return new static($message, $key, $file, $expectedType, $actualValue);
    }

    /**
     * Create exception for invalid configuration value
     *
     * @param string $key
     * @param mixed $value
     * @param array $allowedValues
     * @param string|null $file
     * @return static
     */
    public static function invalidValue(
        string $key,
        $value,
        array $allowedValues = [],
        string $file = null
    ): self {
        $message = "Invalid configuration value for '{$key}': " . var_export($value, true);
        
        if (!empty($allowedValues)) {
            $allowed = implode(', ', array_map(function($v) {
                return var_export($v, true);
            }, $allowedValues));
            $message .= ". Allowed values: {$allowed}";
        }
        
        if ($file) {
            $message .= " in file: {$file}";
        }

        return new static($message, $key, $file, null, $value);
    }

    /**
     * Create exception for configuration file not found
     *
     * @param string $file
     * @return static
     */
    public static function fileNotFound(string $file): self
    {
        return new static(
            "Configuration file not found: {$file}",
            null,
            $file
        );
    }

    /**
     * Create exception for configuration file not readable
     *
     * @param string $file
     * @return static
     */
    public static function fileNotReadable(string $file): self
    {
        return new static(
            "Configuration file is not readable: {$file}",
            null,
            $file
        );
    }

    /**
     * Create exception for invalid configuration file format
     *
     * @param string $file
     * @param string $expectedFormat
     * @return static
     */
    public static function invalidFileFormat(string $file, string $expectedFormat = 'PHP'): self
    {
        return new static(
            "Invalid configuration file format. Expected {$expectedFormat} file: {$file}",
            null,
            $file
        );
    }

    /**
     * Create exception for configuration parsing error
     *
     * @param string $file
     * @param string $parseError
     * @return static
     */
    public static function parseError(string $file, string $parseError): self
    {
        return new static(
            "Configuration file parse error in {$file}: {$parseError}",
            null,
            $file
        );
    }

    /**
     * Create exception for missing environment variable
     *
     * @param string $envVar
     * @return static
     */
    public static function missingEnvironmentVariable(string $envVar): self
    {
        return new static(
            "Missing required environment variable: {$envVar}",
            $envVar
        );
    }

    /**
     * Create exception for invalid environment variable
     *
     * @param string $envVar
     * @param mixed $value
     * @param string $expectedFormat
     * @return static
     */
    public static function invalidEnvironmentVariable(
        string $envVar,
        $value,
        string $expectedFormat
    ): self {
        return new static(
            "Invalid environment variable '{$envVar}': " . var_export($value, true) . 
            ". Expected format: {$expectedFormat}",
            $envVar,
            null,
            $expectedFormat,
            $value
        );
    }

    /**
     * Create exception for circular dependency in configuration
     *
     * @param array $dependencyChain
     * @return static
     */
    public static function circularDependency(array $dependencyChain): self
    {
        $chain = implode(' -> ', $dependencyChain);
        return new static(
            "Circular dependency detected in configuration: {$chain}"
        );
    }

    /**
     * Create exception for configuration validation failure
     *
     * @param string $key
     * @param string $validationRule
     * @param mixed $value
     * @return static
     */
    public static function validationFailed(
        string $key,
        string $validationRule,
        $value
    ): self {
        return new static(
            "Configuration validation failed for '{$key}'. Rule: {$validationRule}, Value: " . 
            var_export($value, true),
            $key,
            null,
            $validationRule,
            $value
        );
    }

    /**
     * Convert to array for debugging
     *
     * @return array
     */
    public function toArray(): array
    {
        return [
            'message' => $this->getMessage(),
            'config_key' => $this->getConfigKey(),
            'config_file' => $this->getConfigFile(),
            'expected_type' => $this->getExpectedType(),
            'actual_value' => $this->getActualValue(),
        ];
    }
}
