<?php
/**
 * SmartThink LMS - Database Configuration
 * 
 * This file contains database connection settings and configuration
 * for the SmartThink Learning Management System.
 */

// Start output buffering early to prevent "headers already sent" errors
if (!ob_get_level()) {
    ob_start();
}

// Database Configuration
define('DB_HOST', 'localhost');
define('DB_NAME', 'smartthink');
define('DB_USER', 'root');
define('DB_PASS', 'pass');
define('DB_CHARSET', 'utf8mb4');

// Application Configuration
define('APP_NAME', 'SmartThink');
define('APP_VERSION', '1.0.0');
define('APP_URL', 'https://demo.superbsystemstechnologies.com');
define('APP_ENV', 'production'); // development, staging, production

// Security Configuration
define('JWT_SECRET', 'your-super-secret-jwt-key-change-in-production');
define('ENCRYPTION_KEY', 'your-32-character-encryption-key');
define('HASH_COST', 12);

// Email Configuration
define('SMTP_HOST', 'smtp.gmail.com');
define('SMTP_PORT', 587);
define('SMTP_USERNAME', 'your-email@gmail.com');
define('SMTP_PASSWORD', 'your-app-password');
define('SMTP_FROM_EMAIL', 'noreply@smartthink.com');
define('SMTP_FROM_NAME', 'SmartThink');

// File Upload Configuration
define('UPLOAD_MAX_SIZE', 10485760); // 10MB in bytes
define('ALLOWED_FILE_TYPES', ['jpg', 'jpeg', 'png', 'gif', 'pdf', 'doc', 'docx', 'mp4', 'avi', 'mov']);
define('UPLOAD_PATH', __DIR__ . '/../uploads/');

// WhatsApp Configuration
define('WHATSAPP_API_URL', 'https://graph.facebook.com/v18.0/');
define('WHATSAPP_PHONE_ID', 'your-whatsapp-phone-id');
define('WHATSAPP_ACCESS_TOKEN', 'your-whatsapp-access-token');
define('WHATSAPP_WEBHOOK_URL', APP_URL . '/api/whatsapp/webhook');

// Payment Configuration
define('STRIPE_PUBLISHABLE_KEY', 'pk_test_your-stripe-publishable-key');
define('STRIPE_SECRET_KEY', 'sk_test_your-stripe-secret-key');
define('PAYPAL_CLIENT_ID', 'your-paypal-client-id');
define('PAYPAL_CLIENT_SECRET', 'your-paypal-client-secret');
// Square configuration (sandbox)
define('SQUARE_APPLICATION_ID', 'sandbox-sq0idb-quQZskYqUe-WeOoNM7PTuA');
define('SQUARE_ACCESS_TOKEN', 'EAAAl2IpgMB5gZqlQ5aY9YPhnyxACUOI0weia469ez0JrS8iy-F3PV0NZtSoemA2');
define('SQUARE_LOCATION_ID', 'LJS6ZQKW6RS4C');
define('SQUARE_API_BASE', 'https://connect.squareupsandbox.com');
// Paystack configuration (set test keys for development)
define('PAYSTACK_PUBLIC_KEY', 'pk_test_2735acfb0f82ca017337195ec136bc5ff8f8d222');
define('PAYSTACK_SECRET_KEY', 'sk_test_0cff35c03c48c92c0055d07264d389ec377b3c87');
define('AI_PREMIUM_PRICE', 99.00);
define('AI_PREMIUM_CURRENCY_PAYSTACK', 'GHS');
define('AI_PREMIUM_CURRENCY_SQUARE', 'USD');
define('OPENAI_API_KEY', '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');
define('OPENAI_MODEL', 'gpt-4o');

// Zoom Configuration (Server-to-Server OAuth)
define('ZOOM_ACCOUNT_ID', 'tSx9qgokRN6Vmdk1MdzFVQ');
define('ZOOM_CLIENT_ID', 'AJwnGwERSbuBxmdNpV0f2g');
define('ZOOM_CLIENT_SECRET', 'S85GKGizWnY8Y0V8yn0cMdBd2ShdgNWY');
define('ZOOM_API_BASE', 'https://api.zoom.us/v2');
define('ZOOM_WEBHOOK_SECRET', 'your-zoom-webhook-secret');

// mNotify SMS Configuration
define('MNOTIFY_API_URL', 'https://api.mnotify.com/api');
define('MNOTIFY_API_KEY', 'your-mnotify-api-key');
define('MNOTIFY_SMS_SENDER', 'SmartThink');
define('MNOTIFY_SMS_ENABLED', true);

// Regional Configuration
define('DEFAULT_REGION', 'usa');
define('SUPPORTED_REGIONS', serialize(['usa', 'europe', 'africa']));

// Session Configuration
define('SESSION_LIFETIME', 7200); // 2 hours in seconds
define('SESSION_PATH', '/');
define('SESSION_DOMAIN', '');
// Set to true only if site has valid HTTPS; false allows HTTP (common on cPanel)
define('SESSION_SECURE', false);
define('SESSION_HTTPONLY', true);

// Explicit session save path for cPanel shared hosting (fallback to system tmp if needed)
$cpanelSessionPath = ini_get('session.save_path');
if (empty($cpanelSessionPath) || !is_writable($cpanelSessionPath)) {
    $altPaths = ['/tmp', sys_get_temp_dir(), __DIR__ . '/../sessions'];
    foreach ($altPaths as $p) {
        if (is_dir($p) && is_writable($p)) {
            ini_set('session.save_path', $p);
            break;
        }
    }
}
// Ensure session directory exists
$savePath = ini_get('session.save_path');
if ($savePath && !is_dir($savePath)) {
    @mkdir($savePath, 0755, true);
}

// Cache Configuration
define('CACHE_DRIVER', 'file'); // file, redis, memcached
define('CACHE_PREFIX', 'smartthink_');
define('CACHE_LIFETIME', 3600); // 1 hour

// Logging Configuration
define('LOG_PATH', __DIR__ . '/../logs/');
define('LOG_LEVEL', 'INFO'); // DEBUG, INFO, WARNING, ERROR, CRITICAL
define('LOG_MAX_FILES', 30);

// API Configuration
define('API_VERSION', 'v1');
define('API_RATE_LIMIT', 100); // requests per hour
define('API_TIMEOUT', 30); // seconds

// Feature Flags
define('FEATURE_GEOLOCATION', true);
define('FEATURE_WHATSAPP', true);
define('FEATURE_AI_RECOMMENDATIONS', true);
define('FEATURE_GAMIFICATION', true);
define('FEATURE_CERTIFICATES', true);

// Error Reporting
if (APP_ENV === 'development') {
    error_reporting(E_ALL);
    ini_set('display_errors', 1);
} else {
    error_reporting(E_ALL & ~E_DEPRECATED & ~E_STRICT);
    ini_set('display_errors', 0);
}

// Timezone
date_default_timezone_set('UTC');

// Character Set
ini_set('default_charset', 'UTF-8');

// Handle session.auto_start on shared hosting (cPanel often enables this)
if (ini_get('session.auto_start') == '1' && session_status() === PHP_SESSION_ACTIVE) {
    session_write_close();
}

// Start Session
if (session_status() === PHP_SESSION_NONE && !headers_sent()) {
    session_set_cookie_params([
        'lifetime' => SESSION_LIFETIME,
        'path' => SESSION_PATH,
        'domain' => SESSION_DOMAIN,
        'secure' => SESSION_SECURE,
        'httponly' => SESSION_HTTPONLY,
        'samesite' => 'Lax'
    ]);
    session_start();
}

/**
 * Database Connection Class
 */
class Database {
    private static $instance = null;
    private $connection;
    
    /**
     * Get database instance (Singleton pattern)
     */
    public static function getInstance() {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }
    
    /**
     * Private constructor to prevent direct instantiation
     */
    private function __construct() {
        try {
            $dsn = "mysql:host=" . DB_HOST . ";dbname=" . DB_NAME . ";charset=" . DB_CHARSET;
            $options = [
                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
                PDO::ATTR_EMULATE_PREPARES => false,
                PDO::ATTR_PERSISTENT => true
            ];
            
            $this->connection = new PDO($dsn, DB_USER, DB_PASS, $options);
        } catch (PDOException $e) {
            if (APP_ENV === 'development') {
                die("Database connection failed: " . $e->getMessage());
            } else {
                die("Database connection failed. Please try again later.");
            }
        }
    }
    
    /**
     * Get database connection
     */
    public function getConnection() {
        return $this->connection;
    }
    
    /**
     * Execute query with prepared statements
     */
    public function query($sql, $params = []) {
        try {
            $stmt = $this->connection->prepare($sql);
            $stmt->execute($params);
            return $stmt;
        } catch (PDOException $e) {
            $this->logError($e->getMessage(), $sql, $params);
            throw $e;
        }
    }
    
    /**
     * Get single record
     */
    public function get($sql, $params = []) {
        $stmt = $this->query($sql, $params);
        return $stmt->fetch();
    }
    
    /**
     * Get multiple records
     */
    public function getAll($sql, $params = []) {
        $stmt = $this->query($sql, $params);
        return $stmt->fetchAll();
    }
    
    /**
     * Insert record
     */
    public function insert($table, $data) {
        $columns = implode(', ', array_keys($data));
        $placeholders = implode(', ', array_fill(0, count($data), '?'));
        $values = array_values($data);
        
        $sql = "INSERT INTO $table ($columns) VALUES ($placeholders)";
        $this->query($sql, $values);
        
        return $this->connection->lastInsertId();
    }

    /**
     * Wrapper to return last insert id (convenience)
     */
    public function getLastInsertId() {
        return $this->connection->lastInsertId();
    }
    
    /**
     * Update record
     */
    public function update($table, $data, $where, $whereParams = []) {
        $setClause = [];
        $values = [];
        
        foreach ($data as $column => $value) {
            $setClause[] = "$column = ?";
            $values[] = $value;
        }
        
        $setClause = implode(', ', $setClause);
        $values = array_merge($values, $whereParams);
        
        $sql = "UPDATE $table SET $setClause WHERE $where";
        $stmt = $this->query($sql, $values);
        
        return $stmt->rowCount();
    }
    
    /**
     * Delete record
     */
    public function delete($table, $where, $params = []) {
        $sql = "DELETE FROM $table WHERE $where";
        $stmt = $this->query($sql, $params);
        
        return $stmt->rowCount();
    }
    
    /**
     * Begin transaction
     */
    public function beginTransaction() {
        return $this->connection->beginTransaction();
    }
    
    /**
     * Commit transaction
     */
    public function commit() {
        return $this->connection->commit();
    }
    
    /**
     * Rollback transaction
     */
    public function rollback() {
        return $this->connection->rollback();
    }
    
    /**
     * Log database errors
     */
    private function logError($message, $sql, $params) {
        $logMessage = sprintf(
            "[%s] Database Error: %s | SQL: %s | Params: %s\n",
            date('Y-m-d H:i:s'),
            $message,
            $sql,
            json_encode($params)
        );
        
        error_log($logMessage, 3, LOG_PATH . 'database.log');
    }
    
    /**
     * Prevent cloning of singleton instance
     */
    private function __clone() {}
    
    /**
     * Prevent unserialization of singleton instance
     */
    public function __wakeup() {
        throw new Exception("Cannot unserialize singleton");
    }
}

/**
 * Utility Functions
 */

/**
 * Generate secure random token
 */
function generateToken($length = 32) {
    return bin2hex(random_bytes($length / 2));
}

/**
 * Hash password securely
 */
function hashPassword($password) {
    $algo = defined('PASSWORD_ARGON2ID') ? PASSWORD_ARGON2ID : PASSWORD_BCRYPT;
    return password_hash($password, $algo, ['cost' => HASH_COST]);
}

/**
 * Verify password
 */
function verifyPassword($password, $hash) {
    return password_verify($password, $hash);
}

/**
 * Sanitize input data
 */
function sanitize($data) {
    if (is_array($data)) {
        return array_map('sanitize', $data);
    }
    return htmlspecialchars(trim($data), ENT_QUOTES, 'UTF-8');
}

/**
 * Validate email
 */
function validateEmail($email) {
    return filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
}

/**
 * Format date for database
 */
function formatDate($date = null) {
    if ($date === null) {
        return date('Y-m-d H:i:s');
    }
    return date('Y-m-d H:i:s', strtotime($date));
}

/**
 * Get client IP address
 */
function getClientIP() {
    $ipKeys = ['HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'REMOTE_ADDR'];
    
    foreach ($ipKeys as $key) {
        if (array_key_exists($key, $_SERVER) === true) {
            foreach (explode(',', $_SERVER[$key]) as $ip) {
                $ip = trim($ip);
                if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false) {
                    return $ip;
                }
            }
        }
    }
    
    return $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
}

/**
 * Get user agent
 */
function getUserAgent() {
    return $_SERVER['HTTP_USER_AGENT'] ?? 'Unknown';
}

function sendEmail($to, $subject, $htmlMessage) {
    $headers = [];
    $headers[] = 'MIME-Version: 1.0';
    $headers[] = 'Content-type: text/html; charset=UTF-8';
    $fromEmail = SMTP_FROM_EMAIL ?? 'noreply@example.com';
    $fromName = SMTP_FROM_NAME ?? APP_NAME;
    $headers[] = 'From: ' . sprintf('%s <%s>', $fromName, $fromEmail);

    $headersString = implode("\r\n", $headers);

    return @mail($to, $subject, $htmlMessage, $headersString);
}

/**
 * Send email with optional file attachments.
 * Falls back to sendEmail() when no attachments are provided.
 */
function sendEmailWithAttachment($to, $subject, $htmlMessage, array $attachments = []) {
    if (empty($attachments)) {
        return sendEmail($to, $subject, $htmlMessage);
    }

    $fromEmail = SMTP_FROM_EMAIL ?? 'noreply@example.com';
    $fromName = SMTP_FROM_NAME ?? APP_NAME;

    $boundary = '=_Part_' . md5(uniqid((string)mt_rand(), true));

    $headers = [];
    $headers[] = 'MIME-Version: 1.0';
    $headers[] = 'From: ' . sprintf('%s <%s>', $fromName, $fromEmail);
    $headers[] = 'Content-Type: multipart/mixed; boundary="' . $boundary . '"';

    $body = '';
    // HTML part
    $body .= '--' . $boundary . "\r\n";
    $body .= "Content-Type: text/html; charset=UTF-8\r\n";
    $body .= "Content-Transfer-Encoding: 7bit\r\n\r\n";
    $body .= $htmlMessage . "\r\n";

    // Attachments
    foreach ($attachments as $att) {
        if (empty($att['path']) || !is_readable($att['path'])) {
            continue;
        }
        $filePath = $att['path'];
        $fileName = $att['filename'] ?? basename($filePath);
        $mimeType = $att['mime'] ?? 'application/octet-stream';

        $fileData = @file_get_contents($filePath);
        if ($fileData === false) {
            continue;
        }

        $body .= '--' . $boundary . "\r\n";
        $body .= 'Content-Type: ' . $mimeType . '; name="' . addslashes($fileName) . '"' . "\r\n";
        $body .= "Content-Transfer-Encoding: base64\r\n";
        $body .= 'Content-Disposition: attachment; filename="' . addslashes($fileName) . '"' . "\r\n\r\n";
        $body .= chunk_split(base64_encode($fileData)) . "\r\n";
    }

    $body .= '--' . $boundary . "--\r\n";

    $headersString = implode("\r\n", $headers);

    return @mail($to, $subject, $body, $headersString);
}

/**
 * Redirect to URL
 */
function redirect($url, $statusCode = 302) {
    if (!headers_sent()) {
        header("Location: $url", true, $statusCode);
        exit;
    }
    // Fallback if headers already sent
    echo '<script>window.location.href="' . htmlspecialchars($url, ENT_QUOTES) . '";</script>';
    echo '<noscript><meta http-equiv="refresh" content="0;url=' . htmlspecialchars($url, ENT_QUOTES) . '"></noscript>';
    exit;
}

/**
 * Set flash message
 */
function setFlashMessage($type, $message) {
    $_SESSION['flash'][$type] = $message;
}

/**
 * Get flash message
 */
function getFlashMessage($type) {
    if (isset($_SESSION['flash'][$type])) {
        $message = $_SESSION['flash'][$type];
        unset($_SESSION['flash'][$type]);
        return $message;
    }
    return null;
}

/**
 * Check if user is logged in
 */
function isLoggedIn() {
    return isset($_SESSION['user_id']) && !empty($_SESSION['user_id']);
}

/**
 * Get current user ID
 */
function getCurrentUserId() {
    return $_SESSION['user_id'] ?? null;
}

/**
 * Get current user data
 */
function getCurrentUser() {
    if (isLoggedIn()) {
        $db = Database::getInstance();
        return $db->get("SELECT * FROM users WHERE id = ?", [getCurrentUserId()]);
    }
    return null;
}

/**
 * Require login
 */
function requireLogin() {
    if (!isLoggedIn()) {
        setFlashMessage('error', 'Please login to access this page.');
        // Always send users to the central LMS login page so this works
        // consistently from both LMS and CMS contexts.
        redirect(APP_URL . '/lms/login.php');
    }

    // Guard against stale sessions where the user has been removed
    // from the database but a user_id is still present in the session.
    $db = Database::getInstance();
    $userExists = $db->get("SELECT id FROM users WHERE id = ?", [getCurrentUserId()]);
    if (!$userExists) {
        // Clear authentication-related session data but keep the session
        // itself so flash messages continue to work.
        unset($_SESSION['user_id'], $_SESSION['role'], $_SESSION['permissions']);
        setFlashMessage('error', 'Your session has expired. Please login again.');
        redirect(APP_URL . '/lms/login.php');
    }

    // Enforce single active session for students based on user_sessions table.
    $role = $_SESSION['role'] ?? null;
    if ($role === 'student') {
        if (session_status() === PHP_SESSION_NONE) {
            session_start();
        }

        $sid = session_id();
        if ($sid) {
            $sessionRow = $db->get(
                "SELECT is_active FROM user_sessions WHERE user_id = ? AND role = 'student' AND session_id = ? ORDER BY id DESC LIMIT 1",
                [getCurrentUserId(), $sid]
            );

            if (!$sessionRow || (int)($sessionRow['is_active'] ?? 0) !== 1) {
                unset($_SESSION['user_id'], $_SESSION['role'], $_SESSION['permissions']);
                setFlashMessage('error', 'Your account was logged in from another device or browser. Please login again.');
                redirect(APP_URL . '/lms/login.php');
            } else {
                $db->update('user_sessions', [
                    'last_seen' => date('Y-m-d H:i:s'),
                ], 'user_id = ? AND role = ? AND session_id = ?', [getCurrentUserId(), 'student', $sid]);
            }
        }
    }
}

/**
 * Check user role
 */
function hasRole($role) {
    // Primary source of truth for the current role is stored in the session
    // by User::createSession(). This avoids relying on a non-existent `role`
    // column on the users table.
    if (isset($_SESSION['role']) && $_SESSION['role']) {
        $currentRole = $_SESSION['role'];

        // Treat super_admin as having admin privileges as well.
        if ($role === 'admin') {
            return in_array($currentRole, ['admin', 'super_admin'], true);
        }

        return $currentRole === $role;
    }

    return false;
}

function hasPermission($permission, $expectedValue = true) {
    if (!isset($_SESSION['permissions']) || $_SESSION['permissions'] === null) {
        return false;
    }

    $permissions = $_SESSION['permissions'];
    if (is_string($permissions)) {
        $decoded = json_decode($permissions, true);
        $permissions = is_array($decoded) ? $decoded : [];
    }

    if (!is_array($permissions)) {
        return false;
    }

    if (isset($permissions['all']) && $permissions['all'] === true) {
        return true;
    }

    if (!array_key_exists($permission, $permissions)) {
        return false;
    }

    if ($expectedValue === null) {
        return true;
    }

    return $permissions[$permission] === $expectedValue;
}

function requireAnyRole($roles) {
    requireLogin();

    $currentRole = $_SESSION['role'] ?? null;
    if (!$currentRole) {
        setFlashMessage('error', 'Access denied.');
        redirect(APP_URL . '/lms/dashboard.php');
    }

    $roles = is_array($roles) ? $roles : [$roles];
    if (!in_array($currentRole, $roles, true)) {
        setFlashMessage('error', 'Access denied.');
        redirect(APP_URL . '/lms/dashboard.php');
    }
}

function requirePermission($permission, $expectedValue = true) {
    requireLogin();

    if (!hasPermission($permission, $expectedValue)) {
        setFlashMessage('error', 'Access denied.');
        redirect(APP_URL . '/lms/dashboard.php');
    }
}

/**
 * Build a region filter for queries where applicable.
 * Returns an array: ['clause' => 'table.region = ?', 'params' => ['africa']]
 * If no region restriction applies, returns ['clause' => '1=1', 'params' => []].
 */
function currentRegionFilter($alias = null) {
    $role = $_SESSION['role'] ?? null;
    $permissions = $_SESSION['permissions'] ?? null;

    // Decode permissions if stored as JSON string
    if (is_string($permissions)) {
        $decoded = json_decode($permissions, true);
        $permissions = is_array($decoded) ? $decoded : [];
    }

    if ($role === 'regional_admin') {
        $region = null;
        if (is_array($permissions) && isset($permissions['manage_region']) && $permissions['manage_region']) {
            $region = $permissions['manage_region'];
        }
        if (!$region) {
            $region = $_SESSION['region'] ?? DEFAULT_REGION;
        }
        $prefix = $alias ? ($alias . '.') : '';
        return ['clause' => $prefix . 'region = ?', 'params' => [$region]];
    }

    return ['clause' => '1=1', 'params' => []];
}

/**
 * Require specific role
 */
function requireRole($role) {
    if (!hasRole($role)) {
        setFlashMessage('error', 'You do not have permission to access this page.');
        redirect('dashboard.php');
    }
}

/**
 * Load configuration from file
 */
function loadConfig($file) {
    if (file_exists($file)) {
        return parse_ini_file($file, true);
    }
    return [];
}

/**
 * Debug function (development only)
 */
function debug($var, $die = false) {
    if (APP_ENV === 'development') {
        echo '<pre>';
        print_r($var);
        echo '</pre>';
        if ($die) {
            die();
        }
    }
}

// Initialize flash messages
if (!isset($_SESSION['flash'])) {
    $_SESSION['flash'] = [];
}

// Auto-load helper files
$helperFiles = [
    __DIR__ . '/helpers/security.php',
    __DIR__ . '/helpers/validation.php',
    __DIR__ . '/helpers/email.php',
    __DIR__ . '/helpers/file.php',
    __DIR__ . '/helpers/notification.php',
    __DIR__ . '/helpers/zoom.php',
];

foreach ($helperFiles as $file) {
    if (file_exists($file)) {
        require_once $file;
    }
}
?>
