<?php

declare(strict_types=1);

namespace CloudXM\SDK\Config;

use CloudXM\SDK\Contracts\ConfigurationInterface;
use CloudXM\SDK\Support\HasLogger;

/**
 * Interface for settings services
 */
interface SettingsServiceInterface
{
    public function get(string $key, mixed $default = null): mixed;
    public function set(string $key, mixed $value): void;
    public function getEnvironment(): string;
    public function all(): array;
}

/**
 * Shared base configuration offering version lookup and debug helpers.
 * Concrete plugin classes extend this and implement plugin-specific pieces.
 */
abstract class AbstractConfiguration implements ConfigurationInterface
{
    use HasLogger;

    private ?string $version = null;

    /* ------------------------------ public API ------------------------------ */

    public function getVersion(): string
    {
        if ($this->version !== null) {
            return $this->version;
        }
        if (!function_exists('get_plugin_data')) {
            require_once ABSPATH . 'wp-admin/includes/plugin.php';
        }
        $data = get_plugin_data($this->getPluginFilePath(), false, false);
        return $this->version = $data['Version'] ?? '0.0.0';
    }

    public function isDebug(): bool
    {
        return defined('WP_DEBUG') && WP_DEBUG;
    }

    public function getEnvironmentType(): string
    {
        if (defined('WP_ENVIRONMENT_TYPE')) {
            return constant('WP_ENVIRONMENT_TYPE');
        }
        return 'production'; // Default fallback
    }

    /* ----------------------- settings functionality ---------------------- */

    /**
     * Load default configuration values
     * Called only when no saved configuration exists
     */
    abstract protected function loadDefaultConfiguration(): array;

    /**
     * Initialize configuration with defaults if not exists
     */
    protected function initializeConfiguration(): void
    {
        $optionName = $this->getOptionName();
        $existing = get_option($optionName, null);

        // Only set defaults if no configuration exists at all
        if ($existing === null || $existing === false) {
            $defaults = $this->loadDefaultConfiguration();
            update_option($optionName, $defaults);
        }
    }

    /**
     * In-memory cache for the WordPress option to avoid repeated get_option calls
     * @var array|null
     */
    private ?array $cachedOption = null;

    /**
     * Get WordPress option value for this plugin
     */
    protected function getWordPressOption(): array
    {
        if ($this->cachedOption !== null) {
            return $this->cachedOption;
        }

        $optionName = $this->getOptionName();
        $settings = get_option($optionName, []);
        $this->cachedOption = is_array($settings) ? $settings : [];
        return $this->cachedOption;
    }

    /**
     * Set WordPress option value for this plugin
     */
    protected function setWordPressOption(array $settings): void
    {
        $this->cachedOption = $settings;
        $optionName = $this->getOptionName();
        update_option($optionName, $settings);
    }

    /**
     * Get a configuration value by key with optional default
     *
     * @param string $key Configuration key (supports dot notation)
     * @param mixed $default Default value if key not found
     * @return mixed Configuration value
     */
    public function get(string $key, $default = null)
    {
        // Support dot notation for nested arrays
        $keys = explode('.', $key);
        $current = $this->getWordPressOption();

        foreach ($keys as $keyPart) {
            if (!is_array($current) || !array_key_exists($keyPart, $current)) {
                return $default;
            }
            $current = $current[$keyPart];
        }

        return $current;
    }

    /**
     * Set a configuration value by key
     *
     * @param string $key Configuration key (supports dot notation)
     * @param mixed $value Configuration value
     */
    public function set(string $key, mixed $value): void
    {
        // Support dot notation for nested arrays
        $keys = explode('.', $key);
        $settings = $this->getWordPressOption();

        $current = &$settings;
        foreach ($keys as $i => $keyPart) {
            if ($i === count($keys) - 1) {
                $current[$keyPart] = $value;
            } else {
                if (!isset($current[$keyPart]) || !is_array($current[$keyPart])) {
                    $current[$keyPart] = [];
                }
                $current = &$current[$keyPart];
            }
        }

        $this->setWordPressOption($settings);
    }

    /**
     * Get all configuration as array
     *
     * @return array All configuration values
     */
    public function all(): array
    {
        return $this->getWordPressOption();
    }

    /**
     * Check if a configuration key exists
     *
     * @param string $key Configuration key
     * @return bool True if key exists, false otherwise
     */
    public function has(string $key): bool
    {
        // Support dot notation for nested arrays
        $keys = explode('.', $key);
        $current = $this->getWordPressOption();

        foreach ($keys as $keyPart) {
            if (!is_array($current) || !array_key_exists($keyPart, $current)) {
                return false;
            }
            $current = $current[$keyPart];
        }

        return true;
    }

    /**
     * Get WordPress option name for this plugin's settings
     */
    abstract protected function getOptionName(): string;

    /**
     * Get the absolute path to the plugin directory
     */
    public function getPluginDirPath(): string
    {
        return plugin_dir_path($this->getPluginFilePath());
    }

    /* ----------------------- abstract methods for child classes ---------------------- */

    /** Plugin name identifier */
    abstract public function getPluginName(): string;

    /**
     * Get the database table prefix for this plugin
     *
     * @return string Table prefix (e.g., 'pluginname_', empty for no prefix)
     */
    public function getTablePrefix(): string
    {
        // Default implementation - subclasses can override
        return $this->getPluginName() . '_';
    }
}
