<?php

declare(strict_types=1);

/**
 * Application Configuration
 *
 * Core application constants, session setup, and helper functions
 * for the Gmind Institute computer education portal.
 */

// ─── Application Constants ───────────────────────────────────────────────────

const APP_NAME = 'Gmind Institute';
const APP_URL  = 'https://advaysingh.co.in/gmind';
const APP_ROOT = __DIR__ . '/..';
const PUBLIC_ROOT = APP_ROOT;

// Subdirectory base path (empty if installed at domain root)
const BASE_PATH = '/gmind';

// ─── Debug Mode ──────────────────────────────────────────────────────────────

const APP_DEBUG = true;

// ─── Session Configuration ───────────────────────────────────────────────────

const SESSION_LIFETIME = 3600;       // 1 hour in seconds
const SESSION_NAME     = 'gmind_session';

// ─── Timezone ────────────────────────────────────────────────────────────────

date_default_timezone_set('Asia/Kolkata');

// ─── Security Functions ──────────────────────────────────────────────────────

/**
 * Generate a CSRF token and store it in the session.
 *
 * Generates a new token if one does not already exist for the current session,
 * ensuring the token persists across requests within the same session.
 *
 * @return string The CSRF token
 */
function generateCsrfToken(): string
{
    if (empty($_SESSION['csrf_token'])) {
        $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
    }

    return $_SESSION['csrf_token'];
}

/**
 * Verify a CSRF token using constant-time comparison.
 *
 * Uses hash_equals() to prevent timing attacks when comparing tokens.
 *
 * @param string $token The token submitted with the form
 * @return bool True if the token is valid, false otherwise
 */
function verifyCsrfToken(string $token): bool
{
    if (empty($_SESSION['csrf_token']) || empty($token)) {
        return false;
    }

    return hash_equals($_SESSION['csrf_token'], $token);
}

// ─── URL Helper ─────────────────────────────────────────────────────────────

/**
 * Generate a URL with the subdirectory base path prepended.
 *
 * Usage: url('/courses') → '/gmind/courses'
 *        url('/')       → '/gmind/'
 *
 * @param string $path The path (should start with /)
 * @return string The full URL path with base path
 */
function url(string $path = '/'): string
{
    $basePath = rtrim(BASE_PATH, '/');
    $path = '/' . ltrim($path, '/');
    return $basePath . $path;
}

/**
 * Generate an asset URL (CSS, JS, images) with the subdirectory base path.
 *
 * Usage: asset('/css/style.css') → '/gmind/css/style.css'
 *
 * @param string $path The asset path (should start with /)
 * @return string The full asset URL path with base path
 */
function asset(string $path): string
{
    return url($path);
}

// ─── Helper Functions ────────────────────────────────────────────────────────

/**
 * Sanitize input data for safe output in HTML.
 *
 * Trims whitespace, applies htmlspecialchars with UTF-8 encoding,
 * and optionally filters through filter_var for additional safety.
 *
 * @param string $input The raw input string
 * @return string The sanitized string
 */
function sanitize(string $input): string
{
    $trimmed = trim($input);
    $escaped = htmlspecialchars($trimmed, ENT_QUOTES | ENT_HTML5, 'UTF-8');
    $filtered = filter_var($escaped, FILTER_SANITIZE_SPECIAL_CHARS);

    return $filtered !== false ? $filtered : $escaped;
}

/**
 * Perform an HTTP redirect and terminate script execution.
 *
 * @param string $url The URL to redirect to (absolute or relative)
 * @return never
 */
function redirect(string $url): never
{
    // Prepend base path if URL is relative (starts with /)
    if (str_starts_with($url, '/') && !str_starts_with($url, BASE_PATH . '/')) {
        $url = url($url);
    }

    // Prevent header injection by stripping newline characters
    $url = str_replace(["\r", "\n", '%0d', '%0a'], '', $url);

    header("Location: {$url}");
    exit;
}

/**
 * Set or retrieve a flash message from the session.
 *
 * When called with a message, it stores the message in the session
 * under the given key. When called without a message, it retrieves
 * and removes the stored message.
 *
 * @param string $key     The flash message key
 * @param string|null $message The message to store, or null to retrieve
 * @return string|null The stored message, or null if none exists
 */
function flash(string $key, ?string $message = null): ?string
{
    if ($message !== null) {
        $_SESSION['flash'][$key] = $message;
        return $message;
    }

    if (isset($_SESSION['flash'][$key])) {
        $msg = $_SESSION['flash'][$key];
        unset($_SESSION['flash'][$key]);
        return $msg;
    }

    return null;
}

/**
 * Convert text into a URL-safe slug.
 *
 * Transliterates non-ASCII characters, converts to lowercase,
 * replaces non-alphanumeric characters with hyphens, and
 * collapses multiple hyphens into one.
 *
 * @param string $text The text to slugify
 * @return string The generated slug
 */
function slugify(string $text): string
{
    // Transliterate non-ASCII characters
    $transliterated = transliterator_transliterate(
        'Any-Latin; Latin-ASCII; Lower()',
        $text
    );

    if ($transliterated === false) {
        $transliterated = $text;
    }

    // Replace non-alphanumeric characters with hyphens
    $slug = preg_replace('/[^a-z0-9]+/i', '-', $transliterated);

    if ($slug === null) {
        $slug = $text;
    }

    // Trim hyphens from ends and collapse multiple hyphens
    $slug = trim($slug, '-');
    $slug = preg_replace('/-+/', '-', $slug);

    return strtolower($slug);
}
