<?php

declare(strict_types=1);

function config(string $key, mixed $default = null): mixed
{
    static $configs = [];

    [$file, $path] = array_pad(explode('.', $key, 2), 2, null);
    if (!array_key_exists($file, $configs)) {
        $configFile = dirname(__DIR__, 2) . '/config/' . $file . '.php';
        $configs[$file] = is_file($configFile) ? require $configFile : [];
    }

    $value = $configs[$file];
    if ($path === null) {
        return $value;
    }

    foreach (explode('.', $path) as $segment) {
        if (!is_array($value) || !array_key_exists($segment, $value)) {
            return $default;
        }
        $value = $value[$segment];
    }

    return $value;
}

function e(mixed $value): string
{
    return htmlspecialchars((string) $value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
}

function url(string $path = ''): string
{
    $basePath = (string) config('app.base_path', '');
    $path = '/' . ltrim($path, '/');
    return ($basePath !== '' ? $basePath : '') . $path;
}

function asset(string $path): string
{
    return url('assets/' . ltrim($path, '/'));
}

function csrf_token(): string
{
    if (empty($_SESSION['_csrf'])) {
        $_SESSION['_csrf'] = bin2hex(random_bytes(32));
    }

    return $_SESSION['_csrf'];
}

function csrf_field(): string
{
    return '<input type="hidden" name="_csrf" value="' . e(csrf_token()) . '">';
}

function verify_csrf(): void
{
    $submitted = $_POST['_csrf'] ?? '';
    if (!is_string($submitted) || !hash_equals(csrf_token(), $submitted)) {
        http_response_code(403);
        render('errors/419', ['title' => '요청을 다시 확인해 주세요']);
        exit;
    }
}

function redirect(string $path, int $status = 302): never
{
    header('Location: ' . url($path), true, $status);
    exit;
}

function render(string $view, array $data = [], string $layout = 'layouts/site'): void
{
    $viewFile = dirname(__DIR__) . '/Views/' . $view . '.php';
    $layoutFile = dirname(__DIR__) . '/Views/' . $layout . '.php';
    if (!is_file($viewFile) || !is_file($layoutFile)) {
        throw new RuntimeException('View not found.');
    }

    extract($data, EXTR_SKIP);
    ob_start();
    require $viewFile;
    $content = (string) ob_get_clean();
    ob_start();
    require $layoutFile;
    $html = (string) ob_get_clean();

    $basePath = (string) config('app.base_path', '');
    if ($basePath !== '') {
        $html = (string) preg_replace_callback(
            '/\b(href|src|action)="(\/(?!\/)[^"]*)"/',
            static function (array $matches) use ($basePath): string {
                $path = $matches[2];
                if ($path === $basePath || str_starts_with($path, $basePath . '/')) return $matches[0];
                return $matches[1] . '="' . $basePath . $path . '"';
            },
            $html
        );
    }
    echo $html;
}

function old(string $key, string $default = ''): string
{
    return e($_POST[$key] ?? $default);
}

function is_active_path(string $path): bool
{
    $current = parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH) ?: '/';
    $basePath = (string) config('app.base_path', '');
    if ($basePath !== '' && ($current === $basePath || str_starts_with($current, $basePath . '/'))) {
        $current = substr($current, strlen($basePath)) ?: '/';
    }
    return $path === '/' ? $current === '/' : str_starts_with($current, $path);
}

function site_setting(string $key, mixed $default = ''): mixed
{
    static $settings = null;
    if ($settings === null) {
        try {
            $settings = (new \App\Repositories\SiteSettingRepository())->all();
        } catch (\Throwable) {
            $settings = [];
        }
    }
    return $settings[$key] ?? $default;
}
