<?php

/**

 * Teacher Portal — auth helper.

 * Include at top of every AJAX endpoint and portal page.

 *

 * SSO: a valid main-portal session (any role) is accepted automatically

 * so teachers don't need to log in twice.

 */

declare(strict_types=1);



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



if (!defined('DB_HOST')) {

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

}



/**

 * Resolve authenticated teacher user ID (users.id), or 0 if not authenticated.

 */

function tp_user_id(): int

{

    // Teacher portal own session

    if (!empty($_SESSION['tp_user_id'])) {

        return (int) $_SESSION['tp_user_id'];

    }

    // SSO from main portal — any logged-in user is allowed

    if (!empty($_SESSION['user_id'])) {

        $_SESSION['tp_user_id']   = (int) $_SESSION['user_id'];

        $_SESSION['tp_user_name'] = $_SESSION['full_name']  ?? '';

        $_SESSION['tp_user_role'] = $_SESSION['role']       ?? '';

        return (int) $_SESSION['user_id'];

    }

    return 0;

}



/**

 * Resolve users.id → staff.id for teacher portal DB queries.

 *

 * timetable_slots.teacher_id and class_subjects.teacher_id both store staff.id,

 * NOT users.id. All AJAX endpoints must use this value, not tp_user_id().

 * Result is cached in $_SESSION['tp_staff_id'] for the lifetime of the session.

 *

 * Lookup order:

 *   1. staff.user_id = users.id  (canonical; set by "Reset Teacher Login")

 *   2. staff.full_name = users.full_name (fallback when user_id link was never set;

 *      auto-repairs staff.user_id so future lookups hit path 1)

 *

 * Returns 0 only if no staff record can be matched at all.

 */

function tp_staff_id(): int

{

    if (!empty($_SESSION['tp_staff_id'])) {

        return (int) $_SESSION['tp_staff_id'];

    }

    $uid = tp_user_id();

    if ($uid === 0) return 0;

    $conn = getDBConnection();



    // Path 1 — canonical link via staff.user_id

    $stmt = $conn->prepare("SELECT id FROM staff WHERE user_id = ? LIMIT 1");

    $stmt->bind_param('i', $uid);

    $stmt->execute();

    $row = $stmt->get_result()->fetch_assoc();

    $stmt->close();

    if ($row) {

        $sid = (int)$row['id'];

        $_SESSION['tp_staff_id'] = $sid;

        return $sid;

    }



    // Path 2 — fallback: match by full_name (handles accounts where staff.user_id is NULL)

    $uname = trim((string)($_SESSION['tp_user_name'] ?? ''));

    if ($uname !== '') {

        $stmt2 = $conn->prepare(

            "SELECT id FROM staff WHERE full_name = ? AND status = 'active' LIMIT 1"

        );

        $stmt2->bind_param('s', $uname);

        $stmt2->execute();

        $row2 = $stmt2->get_result()->fetch_assoc();

        $stmt2->close();

        if ($row2) {

            $sid = (int)$row2['id'];

            // Auto-repair: write user_id so path 1 works on every future request

            $fix = $conn->prepare(

                "UPDATE staff SET user_id = ? WHERE id = ? AND (user_id IS NULL OR user_id = 0)"

            );

            $fix->bind_param('ii', $uid, $sid);

            $fix->execute();

            $fix->close();

            $_SESSION['tp_staff_id'] = $sid;

            return $sid;

        }

    }



    return 0;

}



/**

 * Require authentication — exits with JSON 401/403 if not authed or no staff record.

 * Returns staff.id (NOT users.id) for use in all DB queries.

 */

function tp_require_auth(): int

{

    $uid = tp_user_id();

    if ($uid === 0) {

        http_response_code(401);

        header('Content-Type: application/json');

        echo json_encode(['success' => false, 'message' => 'Not authenticated', 'redirect' => 'login.php']);

        exit;

    }

    $sid = tp_staff_id();

    if ($sid === 0) {

        http_response_code(403);

        header('Content-Type: application/json');

        echo json_encode(['success' => false, 'message' => 'No staff record found for your account. Please contact your administrator.']);

        exit;

    }

    return $sid;

}



/**

 * Require authentication for a portal page — redirects to login if not authed.

 * Returns staff.id.

 *

 * When the user IS authenticated but has no staff record we output an inline

 * error page rather than redirecting — redirecting would cause a loop because

 * login.php auto-redirects any existing session back to index.php.

 */

function tp_require_auth_page(): int

{

    $uid = tp_user_id();

    if ($uid === 0) {

        $back = urlencode($_SERVER['REQUEST_URI'] ?? '');

        header('Location: login.php?next=' . $back);

        exit;

    }

    $sid = tp_staff_id();

    if ($sid === 0) {

        http_response_code(403);

        $name = htmlspecialchars($_SESSION['tp_user_name'] ?? 'your account', ENT_QUOTES);

        echo <<<HTML

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="UTF-8">

<meta name="viewport" content="width=device-width,initial-scale=1">

<title>Staff Record Not Linked — Teacher Portal</title>

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css">

<link rel="stylesheet" href="assets/tp.css">

</head>

<body>

<div class="tp-auth-wrap">

  <div class="tp-auth-card">

    <div class="tp-auth-logo" style="color:#ef4444"><i class="fas fa-user-slash"></i></div>

    <h2 style="font-size:1.15rem;color:#1e293b;margin-bottom:.5rem">Staff Record Not Linked</h2>

    <p style="font-size:.85rem;color:#64748b;line-height:1.6;margin-bottom:1.2rem">

      The user account <strong>{$name}</strong> could not be matched to a staff record.

      <br><br>

      Ask your administrator to open your staff profile in EduTrack, then click

      <strong>Reset Teacher Login</strong> to link the accounts.

      <br><br>

      If you were just added as a user, your full name in <em>Users</em> must exactly

      match your full name in <em>Staff</em>.

    </p>

    <a href="login.php" style="font-size:.85rem;color:#6366f1">← Back to Sign In</a>

  </div>

</div>

</body>

</html>

HTML;

        exit;

    }

    return $sid;

}



/**

 * Send a JSON response and exit.

 */

function tp_json(bool $success, mixed $data = null, string $message = ''): never

{

    header('Content-Type: application/json');

    echo json_encode([

        'success' => $success,

        'message' => $message,

        'data'    => $data,

    ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);

    exit;

}



/**

 * Return the teacher's assigned classes (from both timetable slots and class_subjects).

 * Returns array of distinct class_name strings.

 */

function tp_teacher_classes(int $teacher_id): array

{

    $conn = getDBConnection();

    $ay   = (string)(getSettingValue('academic_year') ?? date('Y'));



    $stmt = $conn->prepare(

        "SELECT DISTINCT class_name FROM (

            SELECT class_name FROM timetable_slots

             WHERE teacher_id = ? AND academic_year = ?

            UNION

            SELECT class_name FROM class_subjects

             WHERE teacher_id = ?

         ) AS t

         ORDER BY class_name"

    );

    $stmt->bind_param('isi', $teacher_id, $ay, $teacher_id);

    $stmt->execute();

    $rows    = $stmt->get_result()->fetch_all(MYSQLI_ASSOC);

    $stmt->close();



    return array_column($rows, 'class_name');

}



/**

 * Return all (class_name, subject_id, subject_name) pairs the teacher is authorised for.

 *

 * Two authority sources, unified:

 *   1. class_subjects  — formal subject assignment (has configured score weights)

 *   2. timetable_slots — timetable-only assignment (subject-based / visiting teachers)

 *

 * class_subjects takes precedence: timetable rows are only included when no

 * matching class_subjects row exists for that (class_name, subject_id) pair.

 */

function tp_teacher_subjects(int $teacher_id): array

{

    $conn = getDBConnection();

    $ay   = (string)(getSettingValue('academic_year') ?? date('Y'));



    $stmt = $conn->prepare(

        "SELECT class_name, subject_id, subject_name

           FROM (

               -- Source 1: formal class_subjects (configured weights)

               SELECT cs.class_name, cs.subject_id, s.subject_name

                 FROM class_subjects cs

                 JOIN subjects s ON s.id = cs.subject_id

                WHERE cs.teacher_id = ?



               UNION



               -- Source 2: timetable-only (subject / visiting teachers)

               -- Excluded when a class_subjects row already covers this pair

               SELECT ts.class_name, ts.subject_id, s.subject_name

                 FROM timetable_slots ts

                 JOIN subjects s ON s.id = ts.subject_id

                WHERE ts.teacher_id    = ?

                  AND ts.subject_id   IS NOT NULL

                  AND ts.academic_year = ?

                  AND NOT EXISTS (

                      SELECT 1 FROM class_subjects cx

                       WHERE cx.class_name  = ts.class_name

                         AND cx.subject_id  = ts.subject_id

                         AND cx.teacher_id  = ?

                  )

           ) AS combined

           ORDER BY class_name, subject_name"

    );

    $stmt->bind_param('iisi', $teacher_id, $teacher_id, $ay, $teacher_id);

    $stmt->execute();

    $rows = $stmt->get_result()->fetch_all(MYSQLI_ASSOC);

    $stmt->close();

    return $rows;

}

