<?php
/**
 * Configuration management with dot notation access.
 *
 * @package PDFDraft
 */

namespace PDFDraft;

if ( ! defined( 'ABSPATH' ) ) {
	exit; // Exit if accessed directly.
}

/**
 * Manages configuration loading and caching.
 */
class Config {
	/**
	 * Loaded configuration cache.
	 *
	 * @var array
	 */
	private static array $cache = array();

	/**
	 * Configuration directory path.
	 *
	 * @var string
	 */
	private string $configDir;

	/**
	 * Constructor.
	 *
	 * @param string|null $configDir Optional configuration directory path.
	 */
	public function __construct( ?string $configDir = null ) {
		$this->configDir = $configDir ?? PDFDRAFT_PLUGIN_DIR . '/configs';
	}

	/**
	 * Get a configuration value using dot notation.
	 *
	 * @param string $key     Configuration key using dot notation.
	 * @param mixed  $default Default value if key does not exist.
	 * @return mixed
	 */
	public function get( string $key, $default = null ) {
		$keys = explode( '.', $key );
		$file = array_shift( $keys );

		$config = $this->load( $file );

		if ( empty( $keys ) ) {
			return $config;
		}

		return $this->getNestedValue( $config, $keys, $default );
	}

	/**
	 * Check if a configuration key exists.
	 *
	 * @param string $key Configuration key.
	 * @return bool
	 */
	public function has( string $key ): bool {
		$keys = explode( '.', $key );
		$file = array_shift( $keys );

		$config = $this->load( $file );

		if ( empty( $keys ) ) {
			return ! empty( $config );
		}

		return $this->hasNestedValue( $config, $keys );
	}

	/**
	 * Get all configuration from a specific file.
	 *
	 * @param string $file Configuration file name without extension.
	 * @return array
	 */
	public function all( string $file ): array {
		return $this->load( $file );
	}

	/**
	 * Load a configuration file.
	 *
	 * @param string $file Configuration file name without extension.
	 * @return array
	 */
	public function load( string $file ): array {
		if ( isset( self::$cache[ $file ] ) ) {
			return self::$cache[ $file ];
		}

		$config_file = $this->configDir . '/' . $file . '.php';

		if ( ! file_exists( $config_file ) ) {
			self::$cache[ $file ] = array();
			return array();
		}

		$config = require $config_file;

		if ( ! is_array( $config ) ) {
			$config = array();
		}

		self::$cache[ $file ] = $config;

		/**
		 * Filter configuration after loading.
		 *
		 * @param array  $config Configuration array.
		 * @param string $file   Configuration file name.
		 */
		return apply_filters( "pdfdraft_config_{$file}", $config, $file );
	}

	/**
	 * Clear the configuration cache.
	 *
	 * @param string|null $file Optional specific file to clear from cache.
	 * @return void
	 */
	public function clearCache( ?string $file = null ): void {
		if ( null === $file ) {
			self::$cache = array();
		} else {
			unset( self::$cache[ $file ] );
		}
	}

	/**
	 * Get a nested value from an array using keys.
	 *
	 * @param array $array   Array to search in.
	 * @param array $keys    Array of keys representing the path.
	 * @param mixed $default Default value if key does not exist.
	 * @return mixed
	 */
	private function getNestedValue( array $array, array $keys, $default = null ) {
		$current = $array;

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

		return $current;
	}

	/**
	 * Check if a nested value exists in an array.
	 *
	 * @param array $array Array to search in.
	 * @param array $keys  Array of keys representing the path.
	 * @return bool
	 */
	private function hasNestedValue( array $array, array $keys ): bool {
		$current = $array;

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

		return true;
	}

	/**
	 * Get singleton instance of Config.
	 *
	 * @return self
	 */
	public static function instance(): self {
		static $instance = null;
		if ( null === $instance ) {
			$instance = new self();
		}
		return $instance;
	}
}
