<?php
declare(strict_types=1);

require_once __DIR__ . '/config.php';

if (session_status() === PHP_SESSION_NONE) {
    session_start();
}

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

if (!function_exists('safe')) {
    function safe($value): string
    {
        return e($value);
    }
}

if (!function_exists('clean')) {
    function clean($value): string
    {
        return trim(strip_tags((string)($value ?? '')));
    }
}

if (!function_exists('safe_ucfirst')) {
    function safe_ucfirst($value): string
    {
        $value = trim((string)($value ?? ''));
        return $value === '' ? '' : ucfirst($value);
    }
}

if (!function_exists('isPost')) {
    function isPost(): bool
    {
        return ($_SERVER['REQUEST_METHOD'] ?? '') === 'POST';
    }
}

if (!function_exists('isGet')) {
    function isGet(): bool
    {
        return ($_SERVER['REQUEST_METHOD'] ?? '') === 'GET';
    }
}

if (!function_exists('redirect')) {
    function redirect(string $path): void
    {
        if (preg_match('#^https?://#i', $path)) {
            header('Location: ' . $path);
            exit;
        }

        $base = defined('BASE_URL') ? rtrim((string)BASE_URL, '/') : '';
        $path = '/' . ltrim($path, '/');

        header('Location: ' . $base . $path);
        exit;
    }
}

if (!function_exists('redirectTo')) {
    function redirectTo(string $path): void
    {
        redirect($path);
    }
}

if (!function_exists('asset')) {
    function asset(string $path): string
    {
        return rtrim(defined('ASSETS_URL') ? (string)ASSETS_URL : '../assets', '/') . '/' . ltrim($path, '/');
    }
}

if (!function_exists('url')) {
    function url(string $path = ''): string
    {
        return rtrim(defined('BASE_URL') ? (string)BASE_URL : '', '/') . '/' . ltrim($path, '/');
    }
}

if (!function_exists('old')) {
    function old(string $key, $default = '')
    {
        return $_POST[$key] ?? $_GET[$key] ?? $default;
    }
}

if (!function_exists('now')) {
    function now(): string
    {
        return date('Y-m-d H:i:s');
    }
}

if (!function_exists('today')) {
    function today(): string
    {
        return date('Y-m-d');
    }
}

if (!function_exists('flash')) {
    function flash(string $key, ?string $message = null): ?string
    {
        if ($message !== null) {
            $_SESSION['flash'][$key] = $message;
            return null;
        }

        $value = $_SESSION['flash'][$key] ?? null;
        unset($_SESSION['flash'][$key]);

        return $value;
    }
}

if (!function_exists('csrfToken')) {
    function csrfToken(): string
    {
        if (empty($_SESSION['_csrf_token'])) {
            $_SESSION['_csrf_token'] = bin2hex(random_bytes(32));
        }

        return $_SESSION['_csrf_token'];
    }
}

if (!function_exists('csrfField')) {
    function csrfField(): string
    {
        return '<input type="hidden" name="csrf_token" value="' . e(csrfToken()) . '">';
    }
}

if (!function_exists('verifyCsrf')) {
    function verifyCsrf(?string $token = null): bool
    {
        $token ??= $_POST['csrf_token'] ?? $_SERVER['HTTP_X_CSRF_TOKEN'] ?? null;
        return is_string($token)
            && $token !== ''
            && hash_equals((string)($_SESSION['_csrf_token'] ?? ''), $token);
    }
}

if (!function_exists('requirePostCsrf')) {
    function requirePostCsrf(): void
    {
        if (isPost() && !verifyCsrf($_POST['csrf_token'] ?? null)) {
            http_response_code(419);
            die('Invalid request token.');
        }
    }
}

if (!function_exists('generateToken')) {
    function generateToken(int $length = 32): string
    {
        $length = max(8, $length);

        if ($length % 2 !== 0) {
            $length++;
        }

        return bin2hex(random_bytes((int)($length / 2)));
    }
}

if (!function_exists('generateCode')) {
    function generateCode(string $prefix = '', int $length = 6): string
    {
        $alphabet = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';
        $out = $prefix;

        for ($i = 0; $i < $length; $i++) {
            $out .= $alphabet[random_int(0, strlen($alphabet) - 1)];
        }

        return $out;
    }
}

if (!function_exists('generateStudentCode')) {
    function generateStudentCode(?PDO $pdo = null): string
    {
        $pdo = $pdo ?: (function_exists('db') ? db() : null);

        do {
            $code = 'SMI' . str_pad((string)random_int(1, 99999), 5, '0', STR_PAD_LEFT);
            $exists = false;

            if ($pdo instanceof PDO && function_exists('tableExists') && tableExists($pdo, 'students')) {
                $stmt = $pdo->prepare('SELECT COUNT(*) FROM students WHERE student_code = ?');
                $stmt->execute([$code]);
                $exists = (int)$stmt->fetchColumn() > 0;
            }
        } while ($exists);

        return $code;
    }
}

if (!function_exists('generateStudentCardNumber')) {
    function generateStudentCardNumber(): string
    {
        $prefix = defined('STUDENT_CARD_PREFIX') ? (string)STUDENT_CARD_PREFIX : 'SMICARD-';
        return $prefix . random_int(100000, 999999);
    }
}

if (!function_exists('generateCourseCode')) {
    function generateCourseCode(): string
    {
        return 'CORE-' . strtoupper(substr(bin2hex(random_bytes(4)), 0, 6));
    }
}

if (!function_exists('generateExamCode')) {
    function generateExamCode(?PDO $pdo = null): string
    {
        $pdo = $pdo ?: (function_exists('db') ? db() : null);
        $prefix = defined('EXAM_CODE_PREFIX') ? (string)EXAM_CODE_PREFIX : 'SMI-EXAM-';

        do {
            $code = $prefix . strtoupper(bin2hex(random_bytes(3)));
            $exists = false;

            if ($pdo instanceof PDO && function_exists('tableExists') && tableExists($pdo, 'core_exams')) {
                $stmt = $pdo->prepare('SELECT COUNT(*) FROM core_exams WHERE exam_code = ?');
                $stmt->execute([$code]);
                $exists = (int)$stmt->fetchColumn() > 0;
            }
        } while ($exists);

        return $code;
    }
}

if (!function_exists('generateCertificateNumber')) {
    function generateCertificateNumber(?PDO $pdo = null): string
    {
        $pdo = $pdo ?: (function_exists('db') ? db() : null);

        do {
            $number = 'SMI-CERT-' . date('Y') . '-' . strtoupper(bin2hex(random_bytes(4)));
            $exists = false;

            if ($pdo instanceof PDO && function_exists('tableExists') && tableExists($pdo, 'core_certificates')) {
                $stmt = $pdo->prepare('SELECT COUNT(*) FROM core_certificates WHERE certificate_number = ?');
                $stmt->execute([$number]);
                $exists = (int)$stmt->fetchColumn() > 0;
            }
        } while ($exists);

        return $number;
    }
}

if (!function_exists('generateVerificationCode')) {
    function generateVerificationCode(): string
    {
        return strtoupper(bin2hex(random_bytes(5)));
    }
}

if (!function_exists('validDate')) {
    function validDate(string $value, string $format = 'Y-m-d'): bool
    {
        $date = DateTimeImmutable::createFromFormat('!' . $format, $value);
        return $date instanceof DateTimeImmutable && $date->format($format) === $value;
    }
}

if (!function_exists('formatDate')) {
    function formatDate($date, string $format = 'M d, Y'): string
    {
        if (empty($date)) {
            return '-';
        }

        $time = strtotime((string)$date);

        return $time ? date($format, $time) : '-';
    }
}

if (!function_exists('formatDateTime')) {
    function formatDateTime($date, string $format = 'M d, Y h:i A'): string
    {
        if (empty($date)) {
            return '-';
        }

        $time = strtotime((string)$date);

        return $time ? date($format, $time) : '-';
    }
}

if (!function_exists('statusBadgeClass')) {

    function statusBadgeClass($status): string
    {
        $status = strtolower((string)($status ?? ''));

        switch ($status) {

            case 'active':
            case 'approved':
            case 'present':
            case 'passed':
            case 'published':
            case 'open':
            case 'submitted':
            case 'paid':
            case 'issued':
            case 'graded':
                return 'success';

            case 'pending':
            case 'late':
            case 'draft':
            case 'scheduled':
            case 'needs_correction':
            case 'partial':
            case 'under_review':
            case 'pending_review':
                return 'warning';

            case 'rejected':
            case 'absent':
            case 'failed':
            case 'blocked':
            case 'closed':
            case 'unpaid':
            case 'cancelled':
            case 'deleted':
                return 'danger';

            default:
                return 'muted';
        }
    }
}

if (!function_exists('percentage')) {
    function percentage($part, $total, int $decimals = 0): float
    {
        $part = (float)($part ?? 0);
        $total = (float)($total ?? 0);

        if ($total <= 0) {
            return 0.0;
        }

        return round(($part / $total) * 100, $decimals);
    }
}
if (!function_exists('gradeLetter')) {

    function gradeLetter(float $percentage): string
    {
        if ($percentage >= 95) {
            return 'A+';
        }

        if ($percentage >= 90) {
            return 'A';
        }

        if ($percentage >= 85) {
            return 'B+';
        }

        if ($percentage >= 80) {
            return 'B';
        }

        if ($percentage >= 75) {
            return 'C+';
        }

        if ($percentage >= 70) {
            return 'C';
        }

        if ($percentage >= 65) {
            return 'D+';
        }

        if ($percentage >= 60) {
            return 'D';
        }

        return 'F';
    }
}

if (!function_exists('gradeGpa')) {

    function gradeGpa(float $percentage): float
    {
        if ($percentage >= 90) {
            return 4.0;
        }

        if ($percentage >= 85) {
            return 3.7;
        }

        if ($percentage >= 80) {
            return 3.3;
        }

        if ($percentage >= 75) {
            return 3.0;
        }

        if ($percentage >= 70) {
            return 2.7;
        }

        if ($percentage >= 65) {
            return 2.3;
        }

        if ($percentage >= 60) {
            return 2.0;
        }

        return 0.0;
    }
}

if (!function_exists('attendanceStatusByMinutes')) {
    function attendanceStatusByMinutes(int $minutes): string
    {
        if ($minutes >= 40) {
            return 'present';
        }

        if ($minutes >= 20) {
            return 'late';
        }

        return 'absent';
    }
}

if (!function_exists('uploadDir')) {
    function uploadDir(string $folder): string
    {
        $base = defined('UPLOADS_PATH') ? (string)UPLOADS_PATH : dirname(__DIR__) . '/uploads';
        $dir = rtrim($base, '/') . '/' . trim($folder, '/');

        if (!is_dir($dir)) {
            @mkdir($dir, 0755, true);
        }

        return $dir;
    }
}

if (!function_exists('uploadUrl')) {
    function uploadUrl(string $path): string
    {
        $path = ltrim($path, '/');

        return rtrim(defined('BASE_URL') ? (string)BASE_URL : '', '/') . '/uploads/' . $path;
    }
}

if (!function_exists('uploadFile')) {
    function uploadFile(array $file, string $directory): ?string
    {
        if (empty($file['name']) || ($file['error'] ?? UPLOAD_ERR_NO_FILE) !== UPLOAD_ERR_OK) {
            return null;
        }

        $tmp = (string)($file['tmp_name'] ?? '');
        if ($tmp === '' || !is_uploaded_file($tmp)) {
            return null;
        }

        $extension = strtolower(pathinfo((string)$file['name'], PATHINFO_EXTENSION));
        $allowedTypes = defined('ALLOWED_FILE_TYPES') ? ALLOWED_FILE_TYPES : [];
        if (!in_array($extension, $allowedTypes, true)) {
            return null;
        }

        $size = (int)($file['size'] ?? 0);
        $maxSize = defined('MAX_UPLOAD_SIZE') ? (int)MAX_UPLOAD_SIZE : 10 * 1024 * 1024;
        if ($size <= 0 || $size > $maxSize) {
            return null;
        }

        $finfo = new finfo(FILEINFO_MIME_TYPE);
        $mime = (string)$finfo->file($tmp);
        $allowedMimes = defined('ALLOWED_UPLOAD_MIMES') ? (ALLOWED_UPLOAD_MIMES[$extension] ?? []) : [];
        if ($allowedMimes !== [] && !in_array($mime, $allowedMimes, true)) {
            return null;
        }

        $sample = file_get_contents($tmp, false, null, 0, 4096);
        if (is_string($sample) && preg_match('/<\?(?:php|=)|<script\b|javascript:/i', $sample)) {
            return null;
        }

        $uploadDir = uploadDir($directory);
        $fileName = generateToken(24) . '.' . $extension;
        $destination = rtrim($uploadDir, '/') . '/' . $fileName;

        if (!move_uploaded_file($tmp, $destination)) {
            return null;
        }
        @chmod($destination, 0644);

        return 'uploads/' . trim($directory, '/') . '/' . $fileName;
    }
}

if (!function_exists('logActivity')) {
    function logActivity(PDO $pdo, string $actorType, int $actorId, string $action, string $description = '', ?int $studentId = null, ?int $courseId = null): void
    {
        try {
            if (!function_exists('tableExists') || !tableExists($pdo, 'core_activity_logs')) {
                return;
            }

            $stmt = $pdo->prepare(
                'INSERT INTO core_activity_logs
                    (user_type, user_id, action, description, ip_address, user_agent, created_at)
                 VALUES (?, ?, ?, ?, ?, ?, NOW())'
            );
            $stmt->execute([
                in_array($actorType, ['admin', 'teacher', 'student'], true) ? $actorType : 'admin',
                $actorId > 0 ? $actorId : null,
                function_exists('mb_substr') ? mb_substr($action, 0, 255) : substr($action, 0, 255),
                $description,
                $_SERVER['REMOTE_ADDR'] ?? null,
                function_exists('mb_substr') ? mb_substr((string)($_SERVER['HTTP_USER_AGENT'] ?? ''), 0, 1000) : substr((string)($_SERVER['HTTP_USER_AGENT'] ?? ''), 0, 1000),
            ]);
        } catch (Throwable $e) {
            error_log('[SMI activity log] ' . $e->getMessage());
        }
    }
}

if (!function_exists('currentFile')) {
    function currentFile(): string
    {
        return basename(parse_url($_SERVER['REQUEST_URI'] ?? '', PHP_URL_PATH) ?: '');
    }
}

if (!function_exists('selected')) {
    function selected($a, $b): string
    {
        return (string)$a === (string)$b ? 'selected' : '';
    }
}

if (!function_exists('checked')) {
    function checked($a, $b = true): string
    {
        return $a == $b ? 'checked' : '';
    }
}

if (!function_exists('jsonResponse')) {
    function jsonResponse(array $data, int $status = 200): never
    {
        http_response_code($status);
        header('Content-Type: application/json; charset=utf-8');
        echo json_encode($data, JSON_UNESCAPED_UNICODE);
        exit;
    }
}

if (!function_exists('currentLang')) {
    function currentLang(): string
    {
        return $_SESSION['lang'] ?? $_SESSION['student_lang'] ?? 'en';
    }
}

if (!function_exists('switchLanguage')) {
    function switchLanguage(string $lang): void
    {
        $_SESSION['lang'] = in_array($lang, ['en', 'ar'], true) ? $lang : 'en';
    }
}

if (!function_exists('levelBadge')) {

    function levelBadge(?string $level): string
    {
        $level = strtolower((string)($level ?? ''));

        if ($level === 'beginner') {
            $label = 'Beginner';
        }

        elseif ($level === 'intermediate') {
            $label = 'Intermediate';
        }

        elseif ($level === 'advanced') {
            $label = 'Advanced';
        }

        else {
            $label = 'Unknown';
        }

        return '<span class="badge ' . e($level ?: 'unknown') . '">' . e($label) . '</span>';
    }
}

if (!function_exists('statusBadge')) {
    function statusBadge(?string $status): string
    {
        $status = strtolower((string)($status ?? 'unknown'));
        $class = statusBadgeClass($status);

        return '<span class="status ' . e($class) . '">' . e(safe_ucfirst($status)) . '</span>';
    }
}

/*
|--------------------------------------------------------------------------
| Compatibility and shared helpers
|--------------------------------------------------------------------------
*/

if (!function_exists('formatDateNice')) {
    function formatDateNice($date, string $format = 'd M Y'): string { return formatDate($date, $format); }
}
if (!function_exists('formatDateTimeNice')) {
    function formatDateTimeNice($date, string $format = 'd M Y, h:i A'): string { return formatDateTime($date, $format); }
}
if (!function_exists('formatTimeNice')) {
    function formatTimeNice($time, string $format = 'h:i A'): string
    {
        if (empty($time)) return '-';
        $stamp = strtotime((string)$time);
        return $stamp ? date($format, $stamp) : '-';
    }
}
if (!function_exists('safeUcfirst')) {
    function safeUcfirst($value): string { return safe_ucfirst($value); }
}
if (!function_exists('getGradeLetter')) {
    function getGradeLetter(float $percentage): string { return gradeLetter($percentage); }
}
if (!function_exists('getPassStatus')) {
    function getPassStatus(float $percentage, float $passMark = 60.0): string { return $percentage >= $passMark ? 'passed' : 'failed'; }
}
if (!function_exists('attendanceStatusByArrival')) {
    function attendanceStatusByArrival(int $minutesAfterStart): string
    {
        return $minutesAfterStart <= ATTENDANCE_PRESENT_WINDOW_MINUTES ? 'present' : 'late';
    }
}
if (!function_exists('appBaseUrl')) {
    function appBaseUrl(): string { return rtrim(BASE_URL, '/'); }
}
if (!function_exists('regenerateSession')) {
    function regenerateSession(): void { if (session_status() === PHP_SESSION_ACTIVE) session_regenerate_id(true); }
}
if (!function_exists('verifyCsrfToken')) {
    function verifyCsrfToken(?string $token = null): bool { return verifyCsrf($token); }
}
if (!function_exists('smiCsrfToken')) {
    function smiCsrfToken(): string { return csrfToken(); }
}
if (!function_exists('smiCheckCsrf')) {
    function smiCheckCsrf(?string $token = null): bool { return verifyCsrf($token); }
}
if (!function_exists('requireStudentLogin')) {
    function requireStudentLogin(): void { if (function_exists('requireStudent')) requireStudent(); }
}
if (!function_exists('requireTeacherLogin')) {
    function requireTeacherLogin(): void { if (function_exists('requireTeacher')) requireTeacher(); }
}
if (!function_exists('requireAdminLogin')) {
    function requireAdminLogin(): void { if (function_exists('requireAdmin')) requireAdmin(); }
}
