<?php
require_once __DIR__ . '/config.php';

// ==================== SESSION MANAGEMENT ====================
// Session is already started in config.php
function startSecureSession() {
    // No-op: session started in config.php
}

// isLoggedIn(), requireLogin(), requireAdmin(), sanitize(), jsonResponse()
// are already defined in config.php — DO NOT redefine here.

function isAdmin() {
    return isLoggedIn() && isset($_SESSION['user_role']) && $_SESSION['user_role'] === 'admin';
}

function loginUser($user) {
    session_regenerate_id(true);
    $_SESSION['user_id']    = $user['id'];
    $_SESSION['user_name']  = $user['name'];
    $_SESSION['user_email'] = $user['email'];
    $_SESSION['user_role']  = $user['role'] ?? 'student';
    $_SESSION['user_token'] = bin2hex(random_bytes(32));
}

function logoutUser() {
    session_unset();
    session_destroy();
}

function getCurrentUser() {
    if (!isLoggedIn()) return null;
    $db = getDB();
    $stmt = $db->prepare("SELECT * FROM users WHERE id = ?");
    $stmt->bind_param("i", $_SESSION['user_id']);
    $stmt->execute();
    return $stmt->get_result()->fetch_assoc();
}

// ==================== PRO / SUBSCRIPTION ====================

/**
 * Get a site_settings value with a fallback default.
 */
function getSiteSetting($key, $default = null) {
    global $pdo;
    try {
        $stmt = $pdo->prepare("SELECT setting_value FROM site_settings WHERE setting_key = ?");
        $stmt->execute([$key]);
        $val = $stmt->fetchColumn();
        return ($val !== false && $val !== null && $val !== '') ? $val : $default;
    } catch (Exception $e) {
        return $default;
    }
}

/**
 * Is this user currently Pro? Accepts a user array (from getCurrentUser()/session fetch)
 * or an int user_id. Also auto-downgrades expired subscriptions.
 */
function isProUser($userOrId) {
    global $pdo;
    if (is_array($userOrId)) {
        $uid = $userOrId['id'];
        $isPro = (int)($userOrId['is_pro'] ?? 0);
        $expires = $userOrId['pro_expires_at'] ?? null;
    } else {
        $uid = (int)$userOrId;
        $stmt = $pdo->prepare("SELECT is_pro, pro_expires_at FROM users WHERE id = ?");
        $stmt->execute([$uid]);
        $row = $stmt->fetch();
        if (!$row) return false;
        $isPro = (int)$row['is_pro'];
        $expires = $row['pro_expires_at'];
    }

    if (!$isPro) return false;

    // NULL expiry = lifetime / admin-granted Pro
    if ($expires === null) return true;

    if (strtotime($expires) < time()) {
        // Expired — auto-downgrade
        $upd = $pdo->prepare("UPDATE users SET is_pro = 0, pro_plan = NULL WHERE id = ?");
        $upd->execute([$uid]);
        return false;
    }
    return true;
}

/** Activate (or extend) Pro for a user. $plan = 'monthly' | 'yearly'. */
function activatePro($userId, $plan) {
    global $pdo;
    $days = $plan === 'yearly' ? 365 : 30;

    $stmt = $pdo->prepare("SELECT pro_expires_at, is_pro FROM users WHERE id = ?");
    $stmt->execute([$userId]);
    $row = $stmt->fetch();

    // Extend from current expiry if still active & in the future, else from now
    $base = time();
    if ($row && !empty($row['pro_expires_at']) && strtotime($row['pro_expires_at']) > time()) {
        $base = strtotime($row['pro_expires_at']);
    }
    $newExpiry = date('Y-m-d H:i:s', strtotime("+{$days} days", $base));

    $upd = $pdo->prepare("UPDATE users SET is_pro = 1, pro_plan = ?, pro_started_at = COALESCE(pro_started_at, NOW()), pro_expires_at = ? WHERE id = ?");
    $upd->execute([$plan, $newExpiry, $userId]);
    return $newExpiry;
}

/** How many AI calls (ai-tips + ai-analysis combined) has this user made today? */
function getAiUsageToday($userId) {
    global $pdo;
    $stmt = $pdo->prepare("SELECT COUNT(*) FROM ai_usage_log WHERE user_id = ? AND DATE(created_at) = CURDATE()");
    $stmt->execute([$userId]);
    return (int)$stmt->fetchColumn();
}

/** Record one AI call for quota purposes. */
function logAiUsage($userId, $feature) {
    global $pdo;
    $stmt = $pdo->prepare("INSERT INTO ai_usage_log (user_id, feature) VALUES (?, ?)");
    $stmt->execute([$userId, $feature]);
}

/**
 * Central quota gate for AI features. Returns:
 *   ['allowed'=>bool, 'is_pro'=>bool, 'used'=>int, 'limit'=>int|null, 'remaining'=>int|null]
 * Pro users get limit=null (unlimited).
 */
function checkAiQuota($userId, $userRow = null) {
    $pro = isProUser($userRow ?? $userId);
    if ($pro) {
        return ['allowed' => true, 'is_pro' => true, 'used' => 0, 'limit' => null, 'remaining' => null];
    }
    $limit = (int)getSiteSetting('free_ai_daily_limit', FREE_AI_DAILY_LIMIT_DEFAULT);
    $used  = getAiUsageToday($userId);
    return [
        'allowed'   => $used < $limit,
        'is_pro'    => false,
        'used'      => $used,
        'limit'     => $limit,
        'remaining' => max(0, $limit - $used),
    ];
}

/**
 * How many times has this user used a given daily-limited feature today?
 * Reuses the ai_usage_log table (feature is a free-form label, not just AI).
 */
function getDailyUsageToday($userId, $feature) {
    global $pdo;
    $stmt = $pdo->prepare("SELECT COUNT(*) FROM ai_usage_log WHERE user_id = ? AND feature = ? AND DATE(created_at) = CURDATE()");
    $stmt->execute([$userId, $feature]);
    return (int)$stmt->fetchColumn();
}

/** Record one use of a daily-limited feature (music search, AI, etc). */
function logDailyUsage($userId, $feature) {
    logAiUsage($userId, $feature);
}

/**
 * Generic daily-quota gate for any free-tier-limited feature (e.g. music search).
 * $settingKey is the site_settings row admins can edit; $defaultLimit is the
 * fallback if that setting is missing. Pro users always get limit=null (unlimited).
 * Returns: ['allowed'=>bool,'is_pro'=>bool,'used'=>int,'limit'=>int|null,'remaining'=>int|null]
 */
function checkDailyQuota($userId, $feature, $settingKey, $defaultLimit, $userRow = null) {
    $pro = isProUser($userRow ?? $userId);
    if ($pro) {
        return ['allowed' => true, 'is_pro' => true, 'used' => 0, 'limit' => null, 'remaining' => null];
    }
    $limit = (int)getSiteSetting($settingKey, $defaultLimit);
    $used  = getDailyUsageToday($userId, $feature);
    return [
        'allowed'   => $used < $limit,
        'is_pro'    => false,
        'used'      => $used,
        'limit'     => $limit,
        'remaining' => max(0, $limit - $used),
    ];
}

/**
 * Free-tier subject-count cap (not a daily quota — a total-owned cap).
 * $settingKey 'free_subject_limit' is admin-editable via Admin → Pro Subscriptions.
 * Returns the same shape as checkDailyQuota() for consistency.
 */
function checkSubjectLimit($userId, $userRow = null) {
    global $pdo;
    $pro = isProUser($userRow ?? $userId);
    if ($pro) {
        return ['allowed' => true, 'is_pro' => true, 'used' => 0, 'limit' => null, 'remaining' => null];
    }
    $limit = (int)getSiteSetting('free_subject_limit', FREE_SUBJECT_LIMIT_DEFAULT);
    $stmt = $pdo->prepare("SELECT COUNT(*) FROM subjects WHERE user_id = ?");
    $stmt->execute([$userId]);
    $used = (int)$stmt->fetchColumn();
    return [
        'allowed'   => $used < $limit,
        'is_pro'    => false,
        'used'      => $used,
        'limit'     => $limit,
        'remaining' => max(0, $limit - $used),
    ];
}

/**
 * Server-side Groq proxy call. Keeps GROQ_API_KEY off the client entirely.
 * $messages = [['role'=>'system'/'user'/'assistant', 'content'=>'...'], ...]
 * Returns the assistant's text, or throws Exception on failure.
 */
function callGroqServer(array $messages, $maxTokens = 1024, $temperature = 0.7) {
    if (!defined('GROQ_API_KEY') || !GROQ_API_KEY) {
        throw new Exception('Groq API key not configured on server.');
    }
    $payload = [
        'model' => 'llama-3.3-70b-versatile',
        'messages' => $messages,
        'max_tokens' => $maxTokens,
        'temperature' => $temperature,
    ];
    $ch = curl_init('https://api.groq.com/openai/v1/chat/completions');
    curl_setopt_array($ch, [
        CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . GROQ_API_KEY, 'Content-Type: application/json'],
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST => true,
        CURLOPT_POSTFIELDS => json_encode($payload),
        CURLOPT_TIMEOUT => 45,
    ]);
    $response = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    $curlErr  = curl_error($ch);
    curl_close($ch);

    if ($response === false) {
        throw new Exception('Groq request failed: ' . $curlErr);
    }
    $data = json_decode($response, true);
    if ($httpCode >= 400) {
        $msg = $data['error']['message'] ?? ('Groq error ' . $httpCode);
        throw new Exception($msg);
    }
    return $data['choices'][0]['message']['content'] ?? '';
}

/** Build the MD5 hash PayHere requires on the checkout form. */
function payhereCheckoutHash($orderId, $amount, $currency = 'LKR') {
    $amountFormatted = number_format((float)$amount, 2, '.', '');
    $secretHash = strtoupper(md5(PAYHERE_MERCHANT_SECRET));
    return strtoupper(md5(PAYHERE_MERCHANT_ID . $orderId . $amountFormatted . $currency . $secretHash));
}

/** Verify the MD5 signature PayHere sends back to the notify_url webhook. */
function payhereVerifyNotifyHash($merchantId, $orderId, $payhereAmount, $payhereCurrency, $statusCode, $md5sig) {
    $amountFormatted = number_format((float)$payhereAmount, 2, '.', '');
    $secretHash = strtoupper(md5(PAYHERE_MERCHANT_SECRET));
    $localSig = strtoupper(md5($merchantId . $orderId . $amountFormatted . $payhereCurrency . $statusCode . $secretHash));
    return hash_equals($localSig, strtoupper($md5sig));
}

// ==================== SECURITY ====================
function generateOTP() {
    return str_pad(rand(100000, 999999), 6, '0', STR_PAD_LEFT);
}

function generateToken() {
    return bin2hex(random_bytes(32));
}

function hashPassword($password) {
    return password_hash($password, PASSWORD_BCRYPT, ['cost' => 12]);
}

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

// ==================== EMAIL (PHPMailer + Brevo) ====================
if (!function_exists('sendEmail')) {
function sendEmail($to, $toName, $subject, $body) {
    // Auto-detect PHPMailer — try both paths
    $base   = dirname(__DIR__);
    $paths  = [
        $base . '/PHPMailer/src/',
        $base . '/vendor/phpmailer/phpmailer/src/',
    ];
    foreach ($paths as $p) {
        if (file_exists($p . 'PHPMailer.php')) {
            require_once $p . 'Exception.php';
            require_once $p . 'PHPMailer.php';
            require_once $p . 'SMTP.php';
            break;
        }
    }
    if (!class_exists('PHPMailer\\PHPMailer\\PHPMailer')) {
        error_log("StudyTrack: PHPMailer not found");
        return false;
    }
    $mail = new \PHPMailer\PHPMailer\PHPMailer(true);
    try {
        $mail->isSMTP();
        $mail->Host       = MAIL_HOST;
        $mail->SMTPAuth   = true;
        $mail->Username   = MAIL_USERNAME;
        $mail->Password   = MAIL_PASSWORD;
        $mail->SMTPSecure = \PHPMailer\PHPMailer\PHPMailer::ENCRYPTION_STARTTLS;
        $mail->Port       = MAIL_PORT;
        $mail->CharSet    = 'UTF-8';
        $mail->setFrom(MAIL_FROM_EMAIL, MAIL_FROM_NAME);
        $mail->addAddress($to, $toName);
        $mail->isHTML(true);
        $mail->Subject = $subject;
        $mail->Body    = $body;
        $mail->AltBody = strip_tags($body);
        $mail->send();
        return true;
    } catch (Exception $e) {
        error_log("Mail error: " . $e->getMessage());
        return false;
    }
}
} // end function_exists sendEmail

if (!function_exists('getEmailTemplate')) {
function getEmailTemplate($subject, $body) {
    return '<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"></head>
<body style="margin:0;padding:0;font-family:Inter,Arial,sans-serif;background:#0f172a;">
<table width="100%" cellpadding="0" cellspacing="0" style="background:#0f172a;padding:40px 20px;">
<tr><td align="center">
<table width="600" cellpadding="0" cellspacing="0" style="background:#1e293b;border-radius:16px;overflow:hidden;border:1px solid #334155;">
<tr><td style="background:linear-gradient(135deg,#6366f1,#8b5cf6);padding:30px;text-align:center;">
<h1 style="color:#fff;margin:0;font-size:28px;font-weight:700;">📚 StudyTrack</h1>
<p style="color:rgba(255,255,255,0.8);margin:8px 0 0;">Your Academic Success Partner</p>
</td></tr>
<tr><td style="padding:40px;color:#e2e8f0;">
<h2 style="color:#f8fafc;margin-top:0;">' . $subject . '</h2>
' . $body . '
</td></tr>
<tr><td style="background:#0f172a;padding:20px;text-align:center;color:#64748b;font-size:13px;">
<p>© ' . date('Y') . ' StudyTrack. All rights reserved.</p>
</td></tr>
</table>
</td></tr>
</table>
</body>
</html>';
}
}

function sendOTPEmail($to, $name, $otp) {
    $body = '<p style="color:#94a3b8;">Hello <strong style="color:#f8fafc;">' . $name . '</strong>,</p>
<p style="color:#94a3b8;">Your verification code is:</p>
<div style="text-align:center;margin:30px 0;">
<span style="background:linear-gradient(135deg,#6366f1,#8b5cf6);color:#fff;font-size:36px;font-weight:700;padding:16px 32px;border-radius:12px;letter-spacing:8px;display:inline-block;">' . $otp . '</span>
</div>
<p style="color:#94a3b8;">This code expires in <strong style="color:#f8fafc;">10 minutes</strong>. Do not share this code with anyone.</p>';
    return sendEmail($to, $name, 'Verify Your Account - StudyTrack', $body);
}

function sendPasswordResetEmail($to, $name, $resetLink) {
    $body = '<p style="color:#94a3b8;">Hello <strong style="color:#f8fafc;">' . $name . '</strong>,</p>
<p style="color:#94a3b8;">We received a request to reset your password. Click the button below:</p>
<div style="text-align:center;margin:30px 0;">
<a href="' . $resetLink . '" style="background:linear-gradient(135deg,#6366f1,#8b5cf6);color:#fff;text-decoration:none;padding:14px 32px;border-radius:8px;font-weight:600;font-size:16px;display:inline-block;">Reset Password</a>
</div>
<p style="color:#94a3b8;font-size:13px;">This link expires in <strong>1 hour</strong>. If you didn\'t request this, ignore this email.</p>';
    return sendEmail($to, $name, 'Reset Your Password - StudyTrack', $body);
}

// ==================== ANALYTICS ====================
function getStudyStats($userId, $period = 'week') {
    $db = getDB();
    
    $dateFilter = match($period) {
        'day'   => 'AND session_date = CURDATE()',
        'week'  => 'AND session_date >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)',
        'month' => 'AND session_date >= DATE_SUB(CURDATE(), INTERVAL 30 DAY)',
        'year'  => 'AND session_date >= DATE_SUB(CURDATE(), INTERVAL 1 YEAR)',
        default => ''
    };
    
    $stmt = $db->prepare("
        SELECT 
            SUM(ss.duration_minutes) as total_minutes,
            COUNT(DISTINCT ss.session_date) as study_days,
            COUNT(ss.id) as total_sessions,
            sub.name as subject_name,
            sub.color as subject_color
        FROM study_sessions ss
        JOIN subjects sub ON ss.subject_id = sub.id
        WHERE ss.user_id = ? $dateFilter
        GROUP BY ss.subject_id
        ORDER BY total_minutes DESC
    ");
    $stmt->bind_param("i", $userId);
    $stmt->execute();
    return $stmt->get_result()->fetch_all(MYSQLI_ASSOC);
}

function getMarkStats($userId) {
    $db = getDB();
    $stmt = $db->prepare("
        SELECT 
            m.*,
            sub.name as subject_name,
            sub.color as subject_color,
            ROUND((m.score/m.max_score)*100, 1) as percentage
        FROM marks m
        JOIN subjects sub ON m.subject_id = sub.id
        WHERE m.user_id = ?
        ORDER BY m.exam_date DESC
        LIMIT 20
    ");
    $stmt->bind_param("i", $userId);
    $stmt->execute();
    return $stmt->get_result()->fetch_all(MYSQLI_ASSOC);
}

function getWeeklyStudyData($userId) {
    $db = getDB();
    $stmt = $db->prepare("
        SELECT 
            DAYNAME(session_date) as day_name,
            SUM(duration_minutes) as total_minutes
        FROM study_sessions
        WHERE user_id = ? AND session_date >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)
        GROUP BY DAYOFWEEK(session_date), day_name
        ORDER BY DAYOFWEEK(session_date)
    ");
    $stmt->bind_param("i", $userId);
    $stmt->execute();
    return $stmt->get_result()->fetch_all(MYSQLI_ASSOC);
}

function getMonthlyMarkTrend($userId) {
    $db = getDB();
    $stmt = $db->prepare("
        SELECT 
            DATE_FORMAT(exam_date, '%b %Y') as month_label,
            ROUND(AVG((score/max_score)*100), 1) as avg_percentage
        FROM marks
        WHERE user_id = ? AND exam_date >= DATE_SUB(CURDATE(), INTERVAL 6 MONTH)
        GROUP BY YEAR(exam_date), MONTH(exam_date)
        ORDER BY exam_date ASC
    ");
    $stmt->bind_param("i", $userId);
    $stmt->execute();
    return $stmt->get_result()->fetch_all(MYSQLI_ASSOC);
}

function getActiveExamCountdown() {
    $db = getDB();
    $result = $db->query("SELECT * FROM exam_dates WHERE is_active = 1 AND exam_date >= CURDATE() ORDER BY exam_date ASC LIMIT 1");
    return $result ? $result->fetch_assoc() : null;
}

function formatDuration($minutes) {
    if ($minutes < 60) return $minutes . 'm';
    $h = floor($minutes / 60);
    $m = $minutes % 60;
    return $h . 'h' . ($m > 0 ? ' ' . $m . 'm' : '');
}

function timeAgo($datetime) {
    $time = strtotime($datetime);
    $diff = time() - $time;
    if ($diff < 60) return 'just now';
    if ($diff < 3600) return floor($diff/60) . 'm ago';
    if ($diff < 86400) return floor($diff/3600) . 'h ago';
    return floor($diff/86400) . 'd ago';
}

// ==================== NOTIFICATIONS ====================
function getUnreadNotificationCount($userId) {
    $db = getDB();
    $stmt = $db->prepare("SELECT COUNT(*) as cnt FROM notifications WHERE (user_id = ? OR user_id IS NULL) AND is_read = 0");
    $stmt->bind_param("i", $userId);
    $stmt->execute();
    $row = $stmt->get_result()->fetch_assoc();
    return (int)($row['cnt'] ?? 0);
}

function getNotifications($userId, $limit = 20) {
    $db = getDB();
    $stmt = $db->prepare("
        SELECT * FROM notifications 
        WHERE user_id = ? OR user_id IS NULL
        ORDER BY created_at DESC
        LIMIT ?
    ");
    $stmt->bind_param("ii", $userId, $limit);
    $stmt->execute();
    return $stmt->get_result()->fetch_all(MYSQLI_ASSOC);
}

function markNotificationRead($notifId, $userId) {
    $db = getDB();
    $stmt = $db->prepare("UPDATE notifications SET is_read = 1 WHERE id = ? AND (user_id = ? OR user_id IS NULL)");
    $stmt->bind_param("ii", $notifId, $userId);
    $stmt->execute();
}

function markAllNotificationsRead($userId) {
    $db = getDB();
    $stmt = $db->prepare("UPDATE notifications SET is_read = 1 WHERE user_id = ? OR user_id IS NULL");
    $stmt->bind_param("i", $userId);
    $stmt->execute();
}
