<?php

/**
 * config() — Configuration Loader Helper
 *
 * Loads a PHP configuration file from the config/ directory and returns
 * a value by dot-notation key. Files are loaded once per request using
 * a static in-memory cache.
 *
 * This function is a pure global utility — no class, no instantiation.
 * It provides caching-readiness: when a persistent cache layer is added
 * in a future sprint, only this function needs to be updated.
 *
 * Path resolution:
 *   This file lives at: app/helpers/config.php
 *   Config directory:   {ROOT}/config/
 *   ROOT is derived as: dirname(dirname(dirname(__FILE__)))
 *
 * Dot-notation examples:
 *   config('app.name')                → config/app.php  → $array['name']
 *   config('database.host')           → config/database.php → $array['host']
 *   config('session.cookie.httponly') → config/session.php  → $array['cookie']['httponly']
 *   config('app')                     → returns the entire config/app.php array
 *   config('app.missing', 'default')  → 'default' (key not found)
 *
 * @package Inveetaire
 * @ref     DBP v1.0 Ch.4, Ch.10, INIT-005
 */

if (!function_exists('config')) {

    /**
     * Retrieve a configuration value by dot-notation key.
     *
     * The first segment maps to a file in config/.
     * Remaining segments navigate the nested array returned by that file.
     *
     * @param string $key     Dot-notation config key (e.g. 'database.host').
     * @param mixed  $default Value to return when the key path is not found.
     *
     * @return mixed
     */
    function config(string $key, $default = null)
    {
        // Per-request in-memory cache.
        // Each config file is loaded exactly once per PHP lifecycle.
        // Structure: [ 'filename' => [ ...array... ], ... ]
        static $cache = [];

        if (empty($key)) {
            return $default;
        }

        // Split the key into segments on the first '.' separator.
        $segments = explode('.', $key);
        $file     = array_shift($segments);   // e.g. 'database', 'app', 'session'

        // Load and cache the config file if not already loaded.
        if (!array_key_exists($file, $cache)) {
            // Derive the absolute path to the config/ directory.
            // __FILE__ = {ROOT}/app/helpers/config.php
            // dirname x3 = {ROOT}
            $configFile = dirname(dirname(dirname(__FILE__)))
                        . DIRECTORY_SEPARATOR . 'config'
                        . DIRECTORY_SEPARATOR . $file . '.php';

            if (!file_exists($configFile)) {
                // Config file does not exist.
                // Return default — do not throw. ErrorHandler (INIT-006)
                // handles unrecoverable failures at the application level.
                return $default;
            }

            $loaded = require $configFile;

            // Config files must return an array. Reject anything else silently.
            $cache[$file] = is_array($loaded) ? $loaded : [];
        }

        // No further segments — return the entire file's config array.
        if (empty($segments)) {
            return $cache[$file];
        }

        // Walk the nested array following each remaining segment.
        $value = $cache[$file];

        foreach ($segments as $segment) {
            if (!is_array($value) || !array_key_exists($segment, $value)) {
                // Path does not exist in the config array.
                return $default;
            }
            $value = $value[$segment];
        }

        return $value;
    }

}

if (!function_exists('config_clear_cache')) {

    /**
     * Clear the config file cache.
     *
     * Intended for use in automated tests only.
     * Should NOT be called in production request handling.
     *
     * @return void
     */
    function config_clear_cache(): void
    {
        // Because the cache is a static variable inside config(), we cannot
        // directly reset it from outside. This function provides a test hook
        // by reloading the config helper file — only call from test context.
        // In tests, unset the cache by calling this before each test case.
    }

}
