<?php
declare(strict_types=1);

/**
 * EnglishSpeak - Teacher Students
 * File: teacher/students/index.php
 */

require __DIR__ . '/../partials/auth.php';
require_once __DIR__ . '/../../config/database.php';

$pdo = getDbConnection();

/*
 * Phạm vi dữ liệu:
 * - admin: toàn hệ thống
 * - teacher: chỉ học sinh/lớp thuộc đúng Tỉnh + Trường trong teacher_profiles
 */
$currentUserId = (int)($_SESSION['user_id'] ?? 0);
$currentUserRole = (string)($_SESSION['user_role'] ?? '');
$teacherScopeProvinceId = 0;
$teacherScopeSchoolId = 0;
$teacherScopeProvince = '';
$teacherScopeSchool = '';
$teacherScopeReady = $currentUserRole === 'admin';

if ($currentUserRole === 'teacher' && $currentUserId > 0) {
    $scopeStmt = $pdo->prepare(
        "SELECT province_id, school_id, province, school_name
         FROM teacher_profiles
         WHERE user_id = :user_id
         LIMIT 1"
    );
    $scopeStmt->execute(['user_id' => $currentUserId]);
    $scopeRow = $scopeStmt->fetch(PDO::FETCH_ASSOC);

    if ($scopeRow) {
        $teacherScopeProvinceId = max(0, (int)($scopeRow['province_id'] ?? 0));
        $teacherScopeSchoolId = max(0, (int)($scopeRow['school_id'] ?? 0));
        $teacherScopeProvince = trim((string)($scopeRow['province'] ?? ''));
        $teacherScopeSchool = trim((string)($scopeRow['school_name'] ?? ''));
        $teacherScopeReady = ($teacherScopeProvinceId > 0 && $teacherScopeSchoolId > 0)
            || ($teacherScopeProvince !== '' && $teacherScopeSchool !== '');
    }
}

function esTeacherScopeSql(string $role, bool $ready): string
{
    global $studentColumns, $teacherScopeProvinceId, $teacherScopeSchoolId;

    if ($role === 'admin') return '';
    if (!$ready) return ' AND 1 = 0';

    // Ưu tiên school_id + province_id; fallback tên cho DB cũ.
    if (isset($studentColumns['province_id'], $studentColumns['school_id'])
        && $teacherScopeProvinceId > 0 && $teacherScopeSchoolId > 0) {
        return " AND province_id = :scope_province_id
                 AND school_id = :scope_school_id";
    }

    return " AND TRIM(province) = :scope_province
             AND TRIM(school_name) = :scope_school";
}

function esTeacherScopeParams(
    string $role,
    bool $ready,
    string $province,
    string $school
): array {
    global $studentColumns, $teacherScopeProvinceId, $teacherScopeSchoolId;

    if ($role === 'admin' || !$ready) return [];

    if (isset($studentColumns['province_id'], $studentColumns['school_id'])
        && $teacherScopeProvinceId > 0 && $teacherScopeSchoolId > 0) {
        return [
            'scope_province_id' => $teacherScopeProvinceId,
            'scope_school_id' => $teacherScopeSchoolId,
        ];
    }

    return [
        'scope_province' => $province,
        'scope_school' => $school,
    ];
}

$pageTitle = 'Students';
$pageSubtitle = 'Manage students and password reset requests';
$activeTeacherNav = 'students';

if (session_status() !== PHP_SESSION_ACTIVE) {
    session_start();
}
if (empty($_SESSION['teacher_csrf_token'])) {
    $_SESSION['teacher_csrf_token'] = bin2hex(random_bytes(32));
}

$flashSuccess = '';
$flashError = '';
$tempPasswordResult = null;
$bulkCreateResults = [];
if ($currentUserRole === 'teacher' && !$teacherScopeReady) {
    $flashError = 'Tài khoản giáo viên chưa có Tỉnh/Trường hợp lệ. Không thể quản lý lớp.';
}

function esTempPassword(int $length = 8): string
{
    // Avoid confusing chars: 0/O, 1/I/l.
    $chars = 'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz23456789';
    $max = strlen($chars) - 1;
    $result = '';
    for ($i = 0; $i < $length; $i++) {
        $result .= $chars[random_int(0, $max)];
    }
    return $result;
}


/** @return array<string,bool> */
function esStudentColumns(PDO $pdo): array
{
    $cols = [];
    foreach ($pdo->query("SHOW COLUMNS FROM students")->fetchAll(PDO::FETCH_ASSOC) as $row) {
        $name = (string)($row['Field'] ?? '');
        if ($name !== '') $cols[$name] = true;
    }
    return $cols;
}

$studentColumns = esStudentColumns($pdo);
$hasGrade = isset($studentColumns['grade']);
$hasClassName = isset($studentColumns['class_name']);
$hasUpdatedAt = isset($studentColumns['updated_at']);
$hasAcademicYearId = isset($studentColumns['academic_year_id']);


/*
 * Lịch sử xếp lớp theo năm học.
 * students.grade / class_name / academic_year_id = thông tin HIỆN TẠI.
 * Bảng này giữ lại lớp của từng học sinh ở từng năm học cũ.
 */
$pdo->exec("
    CREATE TABLE IF NOT EXISTS student_class_history (
        id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
        student_id BIGINT UNSIGNED NOT NULL,
        academic_year_id BIGINT UNSIGNED NOT NULL,
        grade INT NULL,
        class_name VARCHAR(100) NULL,
        province_id BIGINT UNSIGNED NOT NULL DEFAULT 0,
        school_id BIGINT UNSIGNED NOT NULL DEFAULT 0,
        province VARCHAR(150) NULL,
        school_name VARCHAR(255) NULL,
        recorded_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
        UNIQUE KEY uq_student_year (student_id, academic_year_id),
        KEY idx_year_class (academic_year_id, class_name),
        KEY idx_scope_year (province_id, school_id, academic_year_id)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
");

/*
 * Lớp tôi phụ trách:
 * Mỗi Teacher tự khai báo các lớp mình dạy theo từng năm học.
 * Một Teacher có thể phụ trách nhiều lớp / nhiều khối.
 * school_id + academic_year_id + grade + class_name giúp phân biệt lớp trùng tên giữa các trường/năm học.
 */
$pdo->exec("
    CREATE TABLE IF NOT EXISTS teacher_class_assignments (
        id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
        teacher_id BIGINT UNSIGNED NOT NULL,
        school_id BIGINT UNSIGNED NOT NULL,
        academic_year_id BIGINT UNSIGNED NOT NULL,
        grade INT NOT NULL,
        class_name VARCHAR(100) NOT NULL,
        status VARCHAR(20) NOT NULL DEFAULT 'active',
        created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
        updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
        UNIQUE KEY uq_teacher_class_year
            (teacher_id, school_id, academic_year_id, grade, class_name),
        KEY idx_teacher_year (teacher_id, academic_year_id, status),
        KEY idx_school_year_class (school_id, academic_year_id, grade, class_name, status)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
");


function esTableExists(PDO $pdo, string $table): bool
{
    $stmt = $pdo->prepare(
        "SELECT COUNT(*)
         FROM information_schema.tables
         WHERE table_schema = DATABASE()
           AND table_name = :table_name"
    );
    $stmt->execute(['table_name' => $table]);
    return (int)$stmt->fetchColumn() > 0;
}

/** @return array<string,bool> */
function esTableColumns(PDO $pdo, string $table): array
{
    $cols = [];
    if (!esTableExists($pdo, $table)) return $cols;
    foreach ($pdo->query("SHOW COLUMNS FROM `" . str_replace('`', '``', $table) . "`")->fetchAll(PDO::FETCH_ASSOC) as $row) {
        $name = (string)($row['Field'] ?? '');
        if ($name !== '') $cols[$name] = true;
    }
    return $cols;
}

$academicYearOptions = [];
$selectedAcademicYearId = max(0, (int)($_GET['academic_year_id'] ?? 0));
$academicYearLabelColumn = null;

if (esTableExists($pdo, 'academic_years')) {
    $academicYearColumns = esTableColumns($pdo, 'academic_years');
    foreach (['name', 'year_name', 'label', 'title', 'school_year'] as $candidate) {
        if (isset($academicYearColumns[$candidate])) {
            $academicYearLabelColumn = $candidate;
            break;
        }
    }

    if ($academicYearLabelColumn !== null && isset($academicYearColumns['id'])) {
        $orderSql = isset($academicYearColumns['start_date'])
            ? " ORDER BY start_date DESC, id DESC"
            : " ORDER BY id DESC";

        $academicYearOptions = $pdo->query(
            "SELECT id, `" . $academicYearLabelColumn . "` AS year_label FROM academic_years" . $orderSql
        )->fetchAll(PDO::FETCH_ASSOC);

        if ($selectedAcademicYearId <= 0 && $academicYearOptions !== []) {
            if (isset($academicYearColumns['is_active'])) {
                $active = $pdo->query(
                    "SELECT id FROM academic_years WHERE is_active = 1 ORDER BY id DESC LIMIT 1"
                )->fetchColumn();
                if ($active !== false) $selectedAcademicYearId = (int)$active;
            } elseif (isset($academicYearColumns['status'])) {
                $stmt = $pdo->query(
                    "SELECT id FROM academic_years WHERE status IN ('active','current') ORDER BY id DESC LIMIT 1"
                );
                $active = $stmt->fetchColumn();
                if ($active !== false) $selectedAcademicYearId = (int)$active;
            }

            if ($selectedAcademicYearId <= 0) {
                $selectedAcademicYearId = (int)($academicYearOptions[0]['id'] ?? 0);
            }
        }
    }
}


$myClassAssignments = [];
if ($currentUserRole === 'teacher' && $currentUserId > 0 && $selectedAcademicYearId > 0) {
    try {
        $stmt = $pdo->prepare(
            "SELECT id, academic_year_id, grade, class_name, status
             FROM teacher_class_assignments
             WHERE teacher_id = :teacher_id
               AND school_id = :school_id
               AND academic_year_id = :academic_year_id
               AND status = 'active'
             ORDER BY grade ASC, class_name ASC"
        );
        $stmt->execute([
            'teacher_id' => $currentUserId,
            'school_id' => $teacherScopeSchoolId,
            'academic_year_id' => $selectedAcademicYearId,
        ]);
        $myClassAssignments = $stmt->fetchAll(PDO::FETCH_ASSOC);
    } catch (Throwable $e) {
        error_log('Load teacher class assignments error: ' . $e->getMessage());
    }
}

function esTouchSql(bool $hasUpdatedAt): string
{
    return $hasUpdatedAt ? ', updated_at = NOW()' : '';
}


// Ghi lớp hiện tại vào lịch sử của đúng năm học hiện tại.
// Chỉ cập nhật bản ghi của cùng student + cùng năm học.
if ($hasAcademicYearId && $hasClassName) {
    $historyGradeExpr = $hasGrade ? "s.grade" : "NULL";
    $historyProvinceIdExpr = isset($studentColumns['province_id']) ? "COALESCE(s.province_id,0)" : "0";
    $historySchoolIdExpr = isset($studentColumns['school_id']) ? "COALESCE(s.school_id,0)" : "0";
    $historyProvinceExpr = isset($studentColumns['province']) ? "s.province" : "NULL";
    $historySchoolExpr = isset($studentColumns['school_name']) ? "s.school_name" : "NULL";

    $pdo->exec("
        INSERT INTO student_class_history
            (student_id, academic_year_id, grade, class_name,
             province_id, school_id, province, school_name, recorded_at)
        SELECT
            s.id,
            s.academic_year_id,
            {$historyGradeExpr},
            s.class_name,
            {$historyProvinceIdExpr},
            {$historySchoolIdExpr},
            {$historyProvinceExpr},
            {$historySchoolExpr},
            NOW()
        FROM students s
        WHERE s.academic_year_id IS NOT NULL
          AND s.academic_year_id > 0
          AND s.class_name IS NOT NULL
          AND TRIM(s.class_name) <> ''
        ON DUPLICATE KEY UPDATE
            grade = VALUES(grade),
            class_name = VALUES(class_name),
            province_id = VALUES(province_id),
            school_id = VALUES(school_id),
            province = VALUES(province),
            school_name = VALUES(school_name)
    ");
}

if (($_SERVER['REQUEST_METHOD'] ?? 'GET') === 'POST') {
    $csrf = (string)($_POST['csrf_token'] ?? '');
    $action = (string)($_POST['action'] ?? '');

    if (
        $csrf === ''
        || !hash_equals((string)$_SESSION['teacher_csrf_token'], $csrf)
    ) {
        $flashError = 'Yêu cầu không hợp lệ. Vui lòng tải lại trang.';
    } elseif ($action === 'add_academic_year') {
        try {
            if ($currentUserRole !== 'admin') {
                throw new RuntimeException('Chỉ Admin mới được thêm năm học.');
            }
            if (!esTableExists($pdo, 'academic_years')) {
                throw new RuntimeException('Chưa có bảng academic_years.');
            }

            $yearLabel = trim((string)($_POST['year_label'] ?? ''));
            if (!preg_match('/^(\\d{4})-(\\d{4})$/', $yearLabel, $m)) {
                throw new RuntimeException('Năm học phải có dạng 2027-2028.');
            }

            $startYear = (int)$m[1];
            $endYear = (int)$m[2];
            if ($endYear !== $startYear + 1) {
                throw new RuntimeException('Năm học phải là hai năm liên tiếp, ví dụ 2027-2028.');
            }

            $yearColumns = esTableColumns($pdo, 'academic_years');
            $labelColumn = null;
            foreach (['name', 'year_name', 'label', 'title', 'school_year'] as $candidate) {
                if (isset($yearColumns[$candidate])) {
                    $labelColumn = $candidate;
                    break;
                }
            }
            if ($labelColumn === null) {
                throw new RuntimeException('Bảng academic_years chưa có cột tên năm học phù hợp.');
            }

            $checkYear = $pdo->prepare(
                "SELECT id FROM academic_years WHERE `" . $labelColumn . "` = :year_label LIMIT 1"
            );
            $checkYear->execute(['year_label' => $yearLabel]);
            if ($checkYear->fetchColumn() !== false) {
                throw new RuntimeException('Năm học này đã tồn tại.');
            }

            $insertCols = ["`{$labelColumn}`"];
            $insertMarks = [':year_label'];
            $insertParams = ['year_label' => $yearLabel];

            if (isset($yearColumns['start_date'])) {
                $insertCols[] = '`start_date`';
                $insertMarks[] = ':start_date';
                $insertParams['start_date'] = sprintf('%04d-08-01', $startYear);
            }
            if (isset($yearColumns['end_date'])) {
                $insertCols[] = '`end_date`';
                $insertMarks[] = ':end_date';
                $insertParams['end_date'] = sprintf('%04d-07-31', $endYear);
            }
            if (isset($yearColumns['is_active'])) {
                $insertCols[] = '`is_active`';
                $insertMarks[] = ':is_active';
                $insertParams['is_active'] = 0;
            }
            if (isset($yearColumns['status'])) {
                $insertCols[] = '`status`';
                $insertMarks[] = ':status';
                $insertParams['status'] = 'inactive';
            }
            if (isset($yearColumns['created_at'])) {
                $insertCols[] = '`created_at`';
                $insertMarks[] = 'NOW()';
            }
            if (isset($yearColumns['updated_at'])) {
                $insertCols[] = '`updated_at`';
                $insertMarks[] = 'NOW()';
            }

            $insertYear = $pdo->prepare(
                "INSERT INTO academic_years (" . implode(', ', $insertCols) . ")
                 VALUES (" . implode(', ', $insertMarks) . ")"
            );
            $insertYear->execute($insertParams);

            $newYearId = (int)$pdo->lastInsertId();
            $flashSuccess = "Đã thêm năm học {$yearLabel}.";
            $_SESSION['teacher_csrf_token'] = bin2hex(random_bytes(32));

            // Hiển thị ngay năm học vừa tạo sau khi POST.
            if ($newYearId > 0) {
                $selectedAcademicYearId = $newYearId;
            }
        } catch (Throwable $e) {
            error_log('Add academic year error: ' . $e->getMessage());
            $flashError = $e instanceof RuntimeException
                ? $e->getMessage()
                : 'Không thể thêm năm học.';
        }
    } elseif ($action === 'delete_academic_year') {
        try {
            if ($currentUserRole !== 'admin') {
                throw new RuntimeException('Chỉ Admin mới được xóa năm học.');
            }
            if (!esTableExists($pdo, 'academic_years')) {
                throw new RuntimeException('Chưa có bảng academic_years.');
            }

            $deleteYearId = max(0, (int)($_POST['academic_year_id'] ?? 0));
            if ($deleteYearId <= 0) {
                throw new RuntimeException('Năm học không hợp lệ.');
            }

            $yearColumns = esTableColumns($pdo, 'academic_years');
            $labelColumn = null;
            foreach (['name', 'year_name', 'label', 'title', 'school_year'] as $candidate) {
                if (isset($yearColumns[$candidate])) {
                    $labelColumn = $candidate;
                    break;
                }
            }

            $selectCols = ['id'];
            if ($labelColumn !== null) $selectCols[] = "`{$labelColumn}` AS year_label";
            if (isset($yearColumns['is_active'])) $selectCols[] = 'is_active';
            if (isset($yearColumns['status'])) $selectCols[] = 'status';

            $yearStmt = $pdo->prepare(
                'SELECT ' . implode(', ', $selectCols) . ' FROM academic_years WHERE id = :id LIMIT 1'
            );
            $yearStmt->execute(['id' => $deleteYearId]);
            $yearRow = $yearStmt->fetch(PDO::FETCH_ASSOC);
            if (!$yearRow) {
                throw new RuntimeException('Không tìm thấy năm học cần xóa.');
            }

            if (isset($yearRow['is_active']) && (int)$yearRow['is_active'] === 1) {
                throw new RuntimeException('Không thể xóa năm học đang Active. Hãy chuyển sang năm học khác trước.');
            }
            if (isset($yearRow['status']) && in_array(strtolower(trim((string)$yearRow['status'])), ['active', 'current'], true)) {
                throw new RuntimeException('Không thể xóa năm học đang Active. Hãy chuyển sang năm học khác trước.');
            }

            // Chặn xóa nếu bất kỳ bảng nào đang tham chiếu năm học qua cột academic_year_id.
            $refStmt = $pdo->query(
                "SELECT TABLE_NAME
                 FROM information_schema.COLUMNS
                 WHERE TABLE_SCHEMA = DATABASE()
                   AND COLUMN_NAME = 'academic_year_id'
                   AND TABLE_NAME <> 'academic_years'
                 ORDER BY TABLE_NAME"
            );
            $refTables = $refStmt ? $refStmt->fetchAll(PDO::FETCH_COLUMN) : [];
            $usedBy = [];
            foreach ($refTables as $refTable) {
                $refTable = (string)$refTable;
                if ($refTable === '' || !preg_match('/^[A-Za-z0-9_]+$/', $refTable)) continue;
                $countStmt = $pdo->prepare("SELECT COUNT(*) FROM `{$refTable}` WHERE academic_year_id = :year_id");
                $countStmt->execute(['year_id' => $deleteYearId]);
                $count = (int)$countStmt->fetchColumn();
                if ($count > 0) {
                    $usedBy[] = $refTable . ' (' . $count . ')';
                }
            }

            if ($usedBy !== []) {
                throw new RuntimeException(
                    'Không thể xóa năm học vì đã có dữ liệu liên quan: ' . implode(', ', $usedBy) . '. Hãy giữ năm học này để bảo toàn lịch sử.'
                );
            }

            $deleteStmt = $pdo->prepare('DELETE FROM academic_years WHERE id = :id');
            $deleteStmt->execute(['id' => $deleteYearId]);
            if ($deleteStmt->rowCount() < 1) {
                throw new RuntimeException('Không thể xóa năm học.');
            }

            $deletedLabel = trim((string)($yearRow['year_label'] ?? ('ID ' . $deleteYearId)));
            $flashSuccess = 'Đã xóa năm học ' . $deletedLabel . '.';
            $_SESSION['teacher_csrf_token'] = bin2hex(random_bytes(32));

            if ($selectedAcademicYearId === $deleteYearId) {
                $selectedAcademicYearId = 0;
            }
        } catch (Throwable $e) {
            error_log('Delete academic year error: ' . $e->getMessage());
            $flashError = $e instanceof RuntimeException
                ? $e->getMessage()
                : 'Không thể xóa năm học.';
        }
    } elseif ($action === 'add_my_class') {
        try {
            if ($currentUserRole !== 'teacher' || $currentUserId <= 0) {
                throw new RuntimeException('Chỉ Teacher mới được khai báo lớp phụ trách.');
            }
            if (!$teacherScopeReady || $teacherScopeSchoolId <= 0) {
                throw new RuntimeException('Tài khoản Teacher chưa có Trường hợp lệ.');
            }

            $academicYearId = max(0, (int)($_POST['academic_year_id'] ?? 0));
            $grade = max(1, min(5, (int)($_POST['my_class_grade'] ?? 0)));
            $className = trim((string)($_POST['my_class_name'] ?? ''));

            if ($academicYearId <= 0) {
                throw new RuntimeException('Vui lòng chọn năm học.');
            }
            if ($className === '') {
                throw new RuntimeException('Vui lòng nhập tên lớp.');
            }
            if (mb_strlen($className) > 100) {
                throw new RuntimeException('Tên lớp quá dài.');
            }

            $stmt = $pdo->prepare(
                "INSERT INTO teacher_class_assignments
                    (teacher_id, school_id, academic_year_id, grade, class_name, status, created_at, updated_at)
                 VALUES
                    (:teacher_id, :school_id, :academic_year_id, :grade, :class_name, 'active', NOW(), NOW())
                 ON DUPLICATE KEY UPDATE
                    status = 'active',
                    updated_at = NOW()"
            );
            $stmt->execute([
                'teacher_id' => $currentUserId,
                'school_id' => $teacherScopeSchoolId,
                'academic_year_id' => $academicYearId,
                'grade' => $grade,
                'class_name' => $className,
            ]);

            $flashSuccess = "Đã thêm lớp {$className} vào Lớp tôi phụ trách.";
            $selectedAcademicYearId = $academicYearId;
        } catch (Throwable $e) {
            error_log('Add teacher class assignment error: ' . $e->getMessage());
            $flashError = $e instanceof RuntimeException ? $e->getMessage() : 'Không thể thêm lớp phụ trách.';
        }
    } elseif ($action === 'remove_my_class') {
        try {
            if ($currentUserRole !== 'teacher' || $currentUserId <= 0) {
                throw new RuntimeException('Chỉ Teacher mới được thay đổi lớp phụ trách.');
            }

            $assignmentId = max(0, (int)($_POST['assignment_id'] ?? 0));
            if ($assignmentId <= 0) {
                throw new RuntimeException('Lớp phụ trách không hợp lệ.');
            }

            // Chỉ bỏ assignment của chính Teacher; không xóa học sinh và không xóa lịch sử lớp.
            $stmt = $pdo->prepare(
                "UPDATE teacher_class_assignments
                 SET status = 'inactive', updated_at = NOW()
                 WHERE id = :id
                   AND teacher_id = :teacher_id"
            );
            $stmt->execute([
                'id' => $assignmentId,
                'teacher_id' => $currentUserId,
            ]);

            $flashSuccess = 'Đã bỏ lớp khỏi danh sách Lớp tôi phụ trách. Dữ liệu học sinh vẫn được giữ nguyên.';
        } catch (Throwable $e) {
            error_log('Remove teacher class assignment error: ' . $e->getMessage());
            $flashError = $e instanceof RuntimeException ? $e->getMessage() : 'Không thể bỏ lớp phụ trách.';
        }
    } elseif ($action === 'import_students') {
        $postAcademicYearId = max(0, (int)($_POST['academic_year_id'] ?? 0));

        try {
            if (empty($_FILES['student_file']) || !is_array($_FILES['student_file'])) {
                throw new RuntimeException('Hãy chọn file danh sách học sinh.');
            }
            $upload = $_FILES['student_file'];
            if ((int)($upload['error'] ?? UPLOAD_ERR_NO_FILE) !== UPLOAD_ERR_OK) {
                throw new RuntimeException('Không thể tải file lên. Vui lòng chọn lại file.');
            }

            $originalName = (string)($upload['name'] ?? '');
            $tmpName = (string)($upload['tmp_name'] ?? '');
            $ext = strtolower(pathinfo($originalName, PATHINFO_EXTENSION));
            if (!in_array($ext, ['csv', 'xlsx'], true)) {
                throw new RuntimeException('Chỉ hỗ trợ file Excel .xlsx hoặc .csv.');
            }

            $sheetRows = [];
            if ($ext === 'csv') {
                $fh = fopen($tmpName, 'rb');
                if (!$fh) throw new RuntimeException('Không đọc được file CSV.');
                while (($data = fgetcsv($fh)) !== false) $sheetRows[] = $data;
                fclose($fh);
            } else {
                if (!class_exists('ZipArchive')) throw new RuntimeException('PHP chưa bật ZipArchive nên chưa đọc được file .xlsx.');
                $zip = new ZipArchive();
                if ($zip->open($tmpName) !== true) throw new RuntimeException('Không mở được file Excel .xlsx.');
                $shared = [];
                $sharedXml = $zip->getFromName('xl/sharedStrings.xml');
                if ($sharedXml !== false) {
                    $xml = simplexml_load_string($sharedXml);
                    if ($xml) foreach ($xml->si as $si) {
                        $parts = [];
                        if (isset($si->t)) $parts[] = (string)$si->t;
                        foreach ($si->r as $r) $parts[] = (string)$r->t;
                        $shared[] = implode('', $parts);
                    }
                }
                $sheetXml = $zip->getFromName('xl/worksheets/sheet1.xml');
                $zip->close();
                if ($sheetXml === false) throw new RuntimeException('Không tìm thấy Sheet1 trong file Excel.');
                $xml = simplexml_load_string($sheetXml);
                if (!$xml) throw new RuntimeException('File Excel không hợp lệ.');
                foreach ($xml->sheetData->row as $row) {
                    $out = [];
                    foreach ($row->c as $cell) {
                        $ref = (string)$cell['r'];
                        preg_match('/^[A-Z]+/', $ref, $m);
                        $letters = $m[0] ?? 'A';
                        $col = 0;
                        for ($i=0; $i<strlen($letters); $i++) $col = $col * 26 + (ord($letters[$i]) - 64);
                        $col--;
                        $type = (string)$cell['t'];
                        $value = '';
                        if ($type === 's') $value = $shared[(int)$cell->v] ?? '';
                        elseif ($type === 'inlineStr') $value = (string)$cell->is->t;
                        else $value = (string)$cell->v;
                        $out[$col] = trim($value);
                    }
                    if ($out !== []) {
                        ksort($out);
                        $max = max(array_keys($out));
                        $sheetRows[] = array_map(static fn($i) => $out[$i] ?? '', range(0, $max));
                    }
                }
            }

            if ($sheetRows === []) throw new RuntimeException('File không có dữ liệu học sinh.');

            $normalizeHeader = static function(string $v): string {
                $v = mb_strtolower(trim($v), 'UTF-8');
                $map = ['đ'=>'d','á'=>'a','à'=>'a','ả'=>'a','ã'=>'a','ạ'=>'a','ă'=>'a','ắ'=>'a','ằ'=>'a','ẳ'=>'a','ẵ'=>'a','ặ'=>'a','â'=>'a','ấ'=>'a','ầ'=>'a','ẩ'=>'a','ẫ'=>'a','ậ'=>'a','é'=>'e','è'=>'e','ẻ'=>'e','ẽ'=>'e','ẹ'=>'e','ê'=>'e','ế'=>'e','ề'=>'e','ể'=>'e','ễ'=>'e','ệ'=>'e','í'=>'i','ì'=>'i','ỉ'=>'i','ĩ'=>'i','ị'=>'i','ó'=>'o','ò'=>'o','ỏ'=>'o','õ'=>'o','ọ'=>'o','ô'=>'o','ố'=>'o','ồ'=>'o','ổ'=>'o','ỗ'=>'o','ộ'=>'o','ơ'=>'o','ớ'=>'o','ờ'=>'o','ở'=>'o','ỡ'=>'o','ợ'=>'o','ú'=>'u','ù'=>'u','ủ'=>'u','ũ'=>'u','ụ'=>'u','ư'=>'u','ứ'=>'u','ừ'=>'u','ử'=>'u','ữ'=>'u','ự'=>'u','ý'=>'y','ỳ'=>'y','ỷ'=>'y','ỹ'=>'y','ỵ'=>'y'];
                return preg_replace('/[^a-z0-9]+/', '', strtr($v, $map)) ?? '';
            };
            $headers = array_map($normalizeHeader, array_map('strval', $sheetRows[0]));
            $aliases = [
                'full_name'=>['hoten','hovaten','fullname','name'], 'phone'=>['sdt','sodienthoai','phone','telephone'],
                'email'=>['gmail','email'], 'grade'=>['khoi','grade','khoilop'], 'class_name'=>['lop','classname','class'],
                'birth_date'=>['ngaysinh','dateofbirth','birthdate','dob'], 'gender'=>['gioitinh','gender','sex']
            ];
            $indexes = [];
            foreach ($aliases as $field=>$names) foreach ($headers as $i=>$h) if (in_array($h,$names,true)) { $indexes[$field]=$i; break; }
            if (!isset($indexes['full_name'])) throw new RuntimeException('File phải có cột Họ tên.');

            $rows = [];
            foreach (array_slice($sheetRows, 1) as $lineNo=>$data) {
                $get = static fn(string $field) => isset($indexes[$field]) ? trim((string)($data[$indexes[$field]] ?? '')) : '';
                $fullName=$get('full_name'); if ($fullName==='') continue;
                $phone=$get('phone'); $email=$get('email');
                if ($phone==='' && $email==='') { $bulkCreateResults[]=['name'=>$fullName,'login'=>'','password'=>'','status'=>'Bỏ qua: cần SĐT hoặc Gmail','ok'=>false]; continue; }
                if ($email!=='' && !filter_var($email,FILTER_VALIDATE_EMAIL)) { $bulkCreateResults[]=['name'=>$fullName,'login'=>$email,'password'=>'','status'=>'Bỏ qua: Gmail không hợp lệ','ok'=>false]; continue; }
                $gradeRaw=$get('grade');
                $rows[]=['full_name'=>$fullName,'phone'=>$phone,'email'=>$email,'grade'=>$hasGrade && $gradeRaw!==''?max(1,min(5,(int)$gradeRaw)):null,'class_name'=>$get('class_name'),'birth_date'=>$get('birth_date'),'gender'=>$get('gender')];
            }

            $pdo->beginTransaction(); $createdCount=0; $skippedCount=count($bulkCreateResults);
            foreach ($rows as $row) {
                $duplicateWhere=[]; $duplicateParams=[];
                if ($row['phone']!=='' && isset($studentColumns['phone'])) {$duplicateWhere[]='phone = :dup_phone';$duplicateParams['dup_phone']=$row['phone'];}
                if ($row['email']!=='' && isset($studentColumns['email'])) {$duplicateWhere[]='email = :dup_email';$duplicateParams['dup_email']=$row['email'];}
                if ($duplicateWhere) {
                    $check=$pdo->prepare("SELECT id FROM students WHERE ".implode(' OR ',$duplicateWhere)." LIMIT 1"); $check->execute($duplicateParams); $existing=$check->fetch(PDO::FETCH_ASSOC);
                    if ($existing) {$bulkCreateResults[]=['name'=>$row['full_name'],'login'=>$row['phone']!==''?$row['phone']:$row['email'],'password'=>'','status'=>'Bỏ qua: SĐT/Gmail đã tồn tại (ID '.(int)$existing['id'].')','ok'=>false];$skippedCount++;continue;}
                }
                $temporaryPassword=esTempPassword(8); $passwordHash=password_hash($temporaryPassword,PASSWORD_DEFAULT);
                $insertCols=[];$insertMarks=[];$insertParams=[];
                $addField=static function(string $col,mixed $value) use (&$insertCols,&$insertMarks,&$insertParams,$studentColumns):void{if(!isset($studentColumns[$col]))return;$insertCols[]="`{$col}`";$insertMarks[]=':'.$col;$insertParams[$col]=$value;};
                $addField('full_name',$row['full_name']); $addField('phone',$row['phone']!==''?$row['phone']:null); $addField('email',$row['email']!==''?$row['email']:null); $addField('password_hash',$passwordHash);
                if(isset($studentColumns['username']))$addField('username',$row['phone']!==''?$row['phone']:$row['email']);
                if($hasGrade && $row['grade']!==null)$addField('grade',$row['grade']); if($hasClassName)$addField('class_name',$row['class_name']!==''?$row['class_name']:null);
                foreach(['birth_date','date_of_birth','dob'] as $c) if(isset($studentColumns[$c]) && $row['birth_date']!==''){$addField($c,$row['birth_date']);break;}
                foreach(['gender','sex'] as $c) if(isset($studentColumns[$c]) && $row['gender']!==''){$addField($c,$row['gender']);break;}
                if(isset($studentColumns['status']))$addField('status','active'); if($hasAcademicYearId && $postAcademicYearId>0)$addField('academic_year_id',$postAcademicYearId);
                $stmt=$pdo->prepare("INSERT INTO students (".implode(', ',$insertCols).") VALUES (".implode(', ',$insertMarks).")"); $stmt->execute($insertParams);
                $createdCount++; $bulkCreateResults[]=['name'=>$row['full_name'],'login'=>$row['phone']!==''?$row['phone']:$row['email'],'password'=>$temporaryPassword,'status'=>'Đã import','ok'=>true];
            }
            $pdo->commit(); $flashSuccess="Đã import {$createdCount} học sinh".($skippedCount>0?", bỏ qua {$skippedCount} dòng.":'.'); $_SESSION['teacher_csrf_token']=bin2hex(random_bytes(32));
        } catch (Throwable $e) {
            if ($pdo->inTransaction()) $pdo->rollBack();
            error_log('Teacher import students error: '.$e->getMessage());
            $flashError=$e instanceof RuntimeException?$e->getMessage():'Không thể import danh sách học sinh.';
        }
    } elseif ($action === 'assign_students_class') {
        $selectedIds = $_POST['student_ids'] ?? [];
        $targetClass = trim((string)($_POST['assign_class'] ?? ''));
        $targetGrade = $hasGrade ? max(1, min(5, (int)($_POST['assign_grade'] ?? 0))) : null;

        try {
            if (!$hasClassName) {
                throw new RuntimeException('Bảng students chưa có cột class_name.');
            }

            if (!is_array($selectedIds)) {
                $selectedIds = [];
            }

            $studentIds = array_values(array_unique(array_filter(
                array_map(static fn($v): int => max(0, (int)$v), $selectedIds),
                static fn(int $v): bool => $v > 0
            )));

            if ($studentIds === []) {
                throw new RuntimeException('Hãy chọn ít nhất một học sinh.');
            }
            if ($targetClass === '') {
                throw new RuntimeException('Hãy nhập tên lớp, ví dụ 1/1 hoặc 2A.');
            }

            $placeholders = implode(',', array_fill(0, count($studentIds), '?'));

            if ($hasGrade) {
                $sql = "UPDATE students
                        SET grade = ?,
                            class_name = ?" . esTouchSql($hasUpdatedAt) . "
                        WHERE id IN ($placeholders)";
                $params = array_merge([$targetGrade, $targetClass], $studentIds);

                if ($currentUserRole === 'teacher') {
                    if (!$teacherScopeReady) {
                        $sql .= " AND 1 = 0";
                    } elseif (isset($studentColumns['province_id'], $studentColumns['school_id'])
                        && $teacherScopeProvinceId > 0 && $teacherScopeSchoolId > 0) {
                        $sql .= " AND province_id = ? AND school_id = ?";
                        $params[] = $teacherScopeProvinceId;
                        $params[] = $teacherScopeSchoolId;
                    } else {
                        $sql .= " AND TRIM(province) = ? AND TRIM(school_name) = ?";
                        $params[] = $teacherScopeProvince;
                        $params[] = $teacherScopeSchool;
                    }
                }
            } else {
                $sql = "UPDATE students
                        SET class_name = ?" . esTouchSql($hasUpdatedAt) . "
                        WHERE id IN ($placeholders)";
                $params = array_merge([$targetClass], $studentIds);

                if ($currentUserRole === 'teacher') {
                    if (!$teacherScopeReady) {
                        $sql .= " AND 1 = 0";
                    } elseif (isset($studentColumns['province_id'], $studentColumns['school_id'])
                        && $teacherScopeProvinceId > 0 && $teacherScopeSchoolId > 0) {
                        $sql .= " AND province_id = ? AND school_id = ?";
                        $params[] = $teacherScopeProvinceId;
                        $params[] = $teacherScopeSchoolId;
                    } else {
                        $sql .= " AND TRIM(province) = ? AND TRIM(school_name) = ?";
                        $params[] = $teacherScopeProvince;
                        $params[] = $teacherScopeSchool;
                    }
                }
            }

            $stmt = $pdo->prepare($sql);
            $stmt->execute($params);

            $count = $stmt->rowCount();
            $flashSuccess = $hasGrade
                ? "Đã gán {$count} học sinh vào lớp {$targetClass} (Khối {$targetGrade})."
                : "Đã gán {$count} học sinh vào lớp {$targetClass}.";
            $_SESSION['teacher_csrf_token'] = bin2hex(random_bytes(32));
        } catch (Throwable $e) {
            error_log('Teacher assign student class error: ' . $e->getMessage());
            $flashError = $e instanceof RuntimeException ? $e->getMessage() : 'Không thể gán lớp cho học sinh.';
        }
    } elseif ($action === 'archive_student' || $action === 'restore_student') {
        $studentId = max(0, (int)($_POST['student_id'] ?? 0));
        $newStatus = $action === 'archive_student' ? 'suspended' : 'active';

        try {
            if ($studentId <= 0) {
                throw new RuntimeException('Học sinh không hợp lệ.');
            }
            $stmt = $pdo->prepare(
                "UPDATE students
                 SET status = :status" . esTouchSql($hasUpdatedAt) . "
                 WHERE id = :id" . esTeacherScopeSql($currentUserRole, $teacherScopeReady)
            );
            $params = ['status' => $newStatus, 'id' => $studentId];
            $params += esTeacherScopeParams(
                $currentUserRole,
                $teacherScopeReady,
                $teacherScopeProvince,
                $teacherScopeSchool
            );
            $stmt->execute($params);
            if ($stmt->rowCount() < 1) {
                throw new RuntimeException('Không tìm thấy học sinh thuộc phạm vi trường của giáo viên.');
            }
            $flashSuccess = $newStatus === 'suspended'
                ? 'Đã lưu trữ học sinh. Tiến độ, XP và lịch sử học vẫn được giữ nguyên.'
                : 'Đã kích hoạt lại học sinh.';
            $_SESSION['teacher_csrf_token'] = bin2hex(random_bytes(32));
        } catch (Throwable $e) {
            error_log('Teacher archive/restore student error: ' . $e->getMessage());
            $flashError = $e instanceof RuntimeException ? $e->getMessage() : 'Không thể cập nhật học sinh.';
        }
    } elseif ($action === 'bulk_promote_classes') {
        $selectedRows = $_POST['bulk_selected'] ?? [];
        $sourceClasses = $_POST['bulk_source_class'] ?? [];
        $targetGrades = $_POST['bulk_target_grade'] ?? [];
        $targetClasses = $_POST['bulk_target_class'] ?? [];
        $postAcademicYearId = max(0, (int)($_POST['academic_year_id'] ?? 0));
        $targetAcademicYearId = max(0, (int)($_POST['target_academic_year_id'] ?? 0));

        try {
            if (!$hasClassName || !$hasGrade) {
                throw new RuntimeException('Cần có cả cột class_name và grade trong bảng students để lên lớp hàng loạt.');
            }
            if (!is_array($selectedRows) || $selectedRows === []) {
                throw new RuntimeException('Hãy chọn ít nhất một lớp cần lên lớp.');
            }
            if ($hasAcademicYearId) {
                if ($postAcademicYearId <= 0 || $targetAcademicYearId <= 0) {
                    throw new RuntimeException('Hãy chọn năm học hiện tại và năm học mới.');
                }
                if ($targetAcademicYearId === $postAcademicYearId) {
                    throw new RuntimeException('Năm học mới phải khác năm học hiện tại.');
                }
            }

            $selectedRows = array_values(array_unique(array_map('intval', $selectedRows)));
            $plans = [];

            foreach ($selectedRows as $rowIndex) {
                $sourceClass = trim((string)($sourceClasses[$rowIndex] ?? ''));
                $targetGrade = max(1, min(5, (int)($targetGrades[$rowIndex] ?? 0)));
                $targetClass = trim((string)($targetClasses[$rowIndex] ?? ''));

                if ($sourceClass === '') {
                    throw new RuntimeException('Có lớp nguồn không hợp lệ.');
                }
                if ($targetClass === '') {
                    throw new RuntimeException("Hãy nhập lớp mới cho {$sourceClass}.");
                }

                $plans[] = [
                    'source_class' => $sourceClass,
                    'target_grade' => $targetGrade,
                    'target_class' => $targetClass,
                    'student_ids' => [],
                ];
            }

            $pdo->beginTransaction();

            // Snapshot student IDs before updating any class.
            // This prevents chained updates such as 1/1 -> 2/1 and 2/1 -> 3/1
            // from accidentally promoting the first group twice in the same batch.
            foreach ($plans as $i => $plan) {
                $sql = "SELECT id
                        FROM students
                        WHERE class_name = :source_class
                          AND status = 'active'";
                $params = ['source_class' => $plan['source_class']];

                if ($hasAcademicYearId && $postAcademicYearId > 0) {
                    $sql .= " AND academic_year_id = :academic_year_id";
                    $params['academic_year_id'] = $postAcademicYearId;
                }

                $sql .= esTeacherScopeSql($currentUserRole, $teacherScopeReady);
                $params += esTeacherScopeParams(
                    $currentUserRole,
                    $teacherScopeReady,
                    $teacherScopeProvince,
                    $teacherScopeSchool
                );

                $stmt = $pdo->prepare($sql);
                $stmt->execute($params);
                $ids = array_map('intval', $stmt->fetchAll(PDO::FETCH_COLUMN));

                if ($ids === []) {
                    throw new RuntimeException("Lớp {$plan['source_class']} không có học sinh đang hoạt động.");
                }

                $plans[$i]['student_ids'] = $ids;
            }

            $totalStudents = 0;
            $totalClasses = 0;

            foreach ($plans as $plan) {
                $ids = $plan['student_ids'];
                $placeholders = implode(',', array_fill(0, count($ids), '?'));

                // Chốt lớp của năm cũ trước khi chuyển sang năm học mới.
                if ($hasAcademicYearId && $postAcademicYearId > 0) {
                    $historySql = "INSERT INTO student_class_history
                        (student_id, academic_year_id, grade, class_name,
                         province_id, school_id, province, school_name, recorded_at)
                        SELECT
                            id,
                            academic_year_id,
                            grade,
                            class_name, " .
                            (isset($studentColumns['province_id']) ? "COALESCE(province_id,0)" : "0") . ", " .
                            (isset($studentColumns['school_id']) ? "COALESCE(school_id,0)" : "0") . ", " .
                            (isset($studentColumns['province']) ? "province" : "NULL") . ", " .
                            (isset($studentColumns['school_name']) ? "school_name" : "NULL") . ",
                            NOW()
                        FROM students
                        WHERE id IN ($placeholders)
                        ON DUPLICATE KEY UPDATE
                            grade = VALUES(grade),
                            class_name = VALUES(class_name),
                            province_id = VALUES(province_id),
                            school_id = VALUES(school_id),
                            province = VALUES(province),
                            school_name = VALUES(school_name)";
                    $historyStmt = $pdo->prepare($historySql);
                    $historyStmt->execute($ids);
                }

                if ($hasAcademicYearId && $targetAcademicYearId > 0) {
                    $sql = "UPDATE students
                            SET grade = ?,
                                class_name = ?,
                                academic_year_id = ?" . esTouchSql($hasUpdatedAt) . "
                            WHERE id IN ($placeholders)";
                    $params = array_merge(
                        [$plan['target_grade'], $plan['target_class'], $targetAcademicYearId],
                        $ids
                    );
                } else {
                    $sql = "UPDATE students
                            SET grade = ?,
                                class_name = ?" . esTouchSql($hasUpdatedAt) . "
                            WHERE id IN ($placeholders)";
                    $params = array_merge(
                        [$plan['target_grade'], $plan['target_class']],
                        $ids
                    );
                }

                $stmt = $pdo->prepare($sql);
                $stmt->execute($params);

                $totalStudents += count($ids);
                $totalClasses++;
            }

            $pdo->commit();
            $flashSuccess = "Đã lên lớp hàng loạt {$totalClasses} lớp / {$totalStudents} học sinh. Tài khoản, avatar, XP, tiến độ và lịch sử học được giữ nguyên.";
            $_SESSION['teacher_csrf_token'] = bin2hex(random_bytes(32));
        } catch (Throwable $e) {
            if ($pdo->inTransaction()) $pdo->rollBack();
            error_log('Teacher bulk promote classes error: ' . $e->getMessage());
            $flashError = $e instanceof RuntimeException ? $e->getMessage() : 'Không thể lên lớp hàng loạt.';
        }
    } elseif ($action === 'promote_class') {
        $sourceClass = trim((string)($_POST['source_class'] ?? ''));
        $targetClass = trim((string)($_POST['target_class'] ?? ''));
        $postAcademicYearId = max(0, (int)($_POST['academic_year_id'] ?? 0));
        $targetAcademicYearId = max(0, (int)($_POST['target_academic_year_id'] ?? 0));
        $targetGrade = max(1, min(5, (int)($_POST['target_grade'] ?? 0)));

        try {
            if (!$hasClassName || !$hasGrade) {
                throw new RuntimeException('Cần có cả cột class_name và grade trong bảng students để lên lớp.');
            }
            if ($sourceClass === '') {
                throw new RuntimeException('Hãy chọn lớp hiện tại.');
            }
            if ($targetClass === '') {
                throw new RuntimeException('Hãy nhập tên lớp mới, ví dụ 2/1 hoặc 3A.');
            }
            if ($hasAcademicYearId) {
                if ($postAcademicYearId <= 0 || $targetAcademicYearId <= 0) {
                    throw new RuntimeException('Hãy chọn năm học hiện tại và năm học mới.');
                }
                if ($targetAcademicYearId === $postAcademicYearId) {
                    throw new RuntimeException('Năm học mới phải khác năm học hiện tại.');
                }
            }

            $pdo->beginTransaction();

            $countStmt = $pdo->prepare(
                "SELECT COUNT(*) FROM students
                 WHERE class_name = :class_name AND status = 'active'" . ($hasAcademicYearId && $postAcademicYearId > 0 ? " AND academic_year_id = :academic_year_id" : "") . esTeacherScopeSql($currentUserRole, $teacherScopeReady)
            );
            $countParams = ['class_name' => $sourceClass];
            if ($hasAcademicYearId && $postAcademicYearId > 0) $countParams['academic_year_id'] = $postAcademicYearId;
            $countParams += esTeacherScopeParams(
                $currentUserRole,
                $teacherScopeReady,
                $teacherScopeProvince,
                $teacherScopeSchool
            );
            $countStmt->execute($countParams);
            $count = (int)$countStmt->fetchColumn();

            if ($count <= 0) {
                throw new RuntimeException('Lớp này không có học sinh đang hoạt động.');
            }

            // Chốt lớp của năm cũ trước khi lên lớp.
            if ($hasAcademicYearId && $postAcademicYearId > 0) {
                $historySql = "INSERT INTO student_class_history
                    (student_id, academic_year_id, grade, class_name,
                     province_id, school_id, province, school_name, recorded_at)
                    SELECT
                        id,
                        academic_year_id,
                        grade,
                        class_name, " .
                        (isset($studentColumns['province_id']) ? "COALESCE(province_id,0)" : "0") . ", " .
                        (isset($studentColumns['school_id']) ? "COALESCE(school_id,0)" : "0") . ", " .
                        (isset($studentColumns['province']) ? "province" : "NULL") . ", " .
                        (isset($studentColumns['school_name']) ? "school_name" : "NULL") . ",
                        NOW()
                    FROM students
                    WHERE class_name = :history_class
                      AND status = 'active'
                      AND academic_year_id = :history_year" .
                      esTeacherScopeSql($currentUserRole, $teacherScopeReady) . "
                    ON DUPLICATE KEY UPDATE
                        grade = VALUES(grade),
                        class_name = VALUES(class_name),
                        province_id = VALUES(province_id),
                        school_id = VALUES(school_id),
                        province = VALUES(province),
                        school_name = VALUES(school_name)";
                $historyParams = [
                    'history_class' => $sourceClass,
                    'history_year' => $postAcademicYearId,
                ];
                $historyParams += esTeacherScopeParams(
                    $currentUserRole,
                    $teacherScopeReady,
                    $teacherScopeProvince,
                    $teacherScopeSchool
                );
                $historyStmt = $pdo->prepare($historySql);
                $historyStmt->execute($historyParams);
            }

            $update = $pdo->prepare(
                "UPDATE students
                 SET grade = :grade,
                     class_name = :target_class" .
                     ($hasAcademicYearId && $targetAcademicYearId > 0 ? ",
                     academic_year_id = :target_academic_year_id" : "") .
                     esTouchSql($hasUpdatedAt) . "
                 WHERE class_name = :source_class
                   AND status = 'active'" . ($hasAcademicYearId && $postAcademicYearId > 0 ? " AND academic_year_id = :academic_year_id" : "") . esTeacherScopeSql($currentUserRole, $teacherScopeReady)
            );
            $updateParams = [
                'grade' => $targetGrade,
                'target_class' => $targetClass,
                'source_class' => $sourceClass,
            ];
            if ($hasAcademicYearId && $postAcademicYearId > 0) $updateParams['academic_year_id'] = $postAcademicYearId;
            if ($hasAcademicYearId && $targetAcademicYearId > 0) $updateParams['target_academic_year_id'] = $targetAcademicYearId;
            $updateParams += esTeacherScopeParams(
                $currentUserRole,
                $teacherScopeReady,
                $teacherScopeProvince,
                $teacherScopeSchool
            );
            $update->execute($updateParams);

            $pdo->commit();
            $flashSuccess = "Đã cho {$count} học sinh từ lớp {$sourceClass} lên lớp {$targetClass} (Khối {$targetGrade}). Tài khoản, avatar, XP và lịch sử học được giữ nguyên.";
            $_SESSION['teacher_csrf_token'] = bin2hex(random_bytes(32));
        } catch (Throwable $e) {
            if ($pdo->inTransaction()) $pdo->rollBack();
            error_log('Teacher promote class error: ' . $e->getMessage());
            $flashError = $e instanceof RuntimeException ? $e->getMessage() : 'Không thể lên lớp.';
        }
    } elseif ($action === 'reset_student_password_fixed') {
        $studentId = max(0, (int)($_POST['student_id'] ?? 0));
        try {
            if ($studentId <= 0) throw new RuntimeException('Học sinh không hợp lệ.');
            if (!isset($studentColumns['password_hash'])) throw new RuntimeException('Bảng students chưa có cột password_hash.');

            $checkSql = "SELECT id, full_name FROM students WHERE id = :id" . esTeacherScopeSql($currentUserRole, $teacherScopeReady) . " LIMIT 1";
            $check = $pdo->prepare($checkSql);
            $params = ['id' => $studentId] + esTeacherScopeParams($currentUserRole, $teacherScopeReady, $teacherScopeProvince, $teacherScopeSchool);
            $check->execute($params);
            $studentRow = $check->fetch(PDO::FETCH_ASSOC);
            if (!$studentRow) throw new RuntimeException('Không tìm thấy học sinh trong phạm vi được quản lý.');

            $newPassword = '12345678';
            $update = $pdo->prepare("UPDATE students SET password_hash = :password_hash" . esTouchSql($hasUpdatedAt) . " WHERE id = :id");
            $update->execute(['password_hash' => password_hash($newPassword, PASSWORD_DEFAULT), 'id' => $studentId]);
            $flashSuccess = 'Đã reset mật khẩu của ' . (string)$studentRow['full_name'] . ' về 12345678.';
            $_SESSION['teacher_csrf_token'] = bin2hex(random_bytes(32));
        } catch (Throwable $e) {
            error_log('Direct reset student password error: ' . $e->getMessage());
            $flashError = $e instanceof RuntimeException ? $e->getMessage() : 'Không thể reset mật khẩu học sinh.';
        }
    } elseif ($action === 'delete_student') {
        $studentId = max(0, (int)($_POST['student_id'] ?? 0));
        try {
            if ($currentUserRole !== 'admin') throw new RuntimeException('Chỉ Admin mới được xóa tài khoản học sinh.');
            if ($studentId <= 0) throw new RuntimeException('Học sinh không hợp lệ.');

            $check = $pdo->prepare("SELECT id, full_name FROM students WHERE id = :id LIMIT 1");
            $check->execute(['id' => $studentId]);
            $studentRow = $check->fetch(PDO::FETCH_ASSOC);
            if (!$studentRow) throw new RuntimeException('Không tìm thấy học sinh.');

            // Không xóa học sinh đã phát sinh dữ liệu học; dùng Lưu trữ để giữ lịch sử.
            foreach (['student_content_progress', 'xp_transactions', 'student_activity_attempts'] as $learningTable) {
                if (!esTableExists($pdo, $learningTable)) continue;
                $cols = esTableColumns($pdo, $learningTable);
                if (!isset($cols['student_id'])) continue;
                $countStmt = $pdo->prepare("SELECT COUNT(*) FROM `{$learningTable}` WHERE student_id = :student_id");
                $countStmt->execute(['student_id' => $studentId]);
                if ((int)$countStmt->fetchColumn() > 0) {
                    throw new RuntimeException('Học sinh đã có XP/tiến độ/lịch sử học. Hãy dùng Lưu trữ thay vì Xóa.');
                }
            }

            $pdo->beginTransaction();
            if (esTableExists($pdo, 'password_reset_requests')) {
                $stmt = $pdo->prepare("DELETE FROM password_reset_requests WHERE student_id = :student_id");
                $stmt->execute(['student_id' => $studentId]);
            }
            if (esTableExists($pdo, 'student_class_history')) {
                $stmt = $pdo->prepare("DELETE FROM student_class_history WHERE student_id = :student_id");
                $stmt->execute(['student_id' => $studentId]);
            }
            $stmt = $pdo->prepare("DELETE FROM students WHERE id = :id");
            $stmt->execute(['id' => $studentId]);
            $pdo->commit();
            $flashSuccess = 'Đã xóa tài khoản học sinh ' . (string)$studentRow['full_name'] . '.';
            $_SESSION['teacher_csrf_token'] = bin2hex(random_bytes(32));
        } catch (Throwable $e) {
            if ($pdo->inTransaction()) $pdo->rollBack();
            error_log('Delete student error: ' . $e->getMessage());
            $flashError = $e instanceof RuntimeException ? $e->getMessage() : 'Không thể xóa học sinh.';
        }
    } elseif ($action === 'reset_password') {
        $requestId = max(0, (int)($_POST['request_id'] ?? 0));

        try {
            $pdo->beginTransaction();

            $stmt = $pdo->prepare(
                "SELECT
                    r.id AS request_id,
                    r.student_id,
                    r.status AS request_status,
                    s.full_name,
                    s.phone,
                    s.email
                 FROM password_reset_requests r
                 INNER JOIN students s ON s.id = r.student_id
                 WHERE r.id = :request_id" .
                 esTeacherScopeSql($currentUserRole, $teacherScopeReady) . "
                 FOR UPDATE"
            );
            $resetParams = ['request_id' => $requestId];
            $resetParams += esTeacherScopeParams(
                $currentUserRole,
                $teacherScopeReady,
                $teacherScopeProvince,
                $teacherScopeSchool
            );
            $stmt->execute($resetParams);
            $row = $stmt->fetch(PDO::FETCH_ASSOC);

            if (!$row) {
                throw new RuntimeException('Không tìm thấy yêu cầu đặt lại mật khẩu.');
            }
            if (($row['request_status'] ?? '') !== 'pending') {
                throw new RuntimeException('Yêu cầu này đã được xử lý.');
            }

            $temporaryPassword = esTempPassword(8);
            $passwordHash = password_hash($temporaryPassword, PASSWORD_DEFAULT);

            $updateStudent = $pdo->prepare(
                "UPDATE students
                 SET password_hash = :password_hash,
                     updated_at = NOW()
                 WHERE id = :student_id"
            );
            $updateStudent->execute([
                'password_hash' => $passwordHash,
                'student_id' => (int)$row['student_id'],
            ]);

            $updateRequest = $pdo->prepare(
                "UPDATE password_reset_requests
                 SET status = 'completed',
                     completed_at = NOW(),
                     updated_at = NOW()
                 WHERE id = :request_id"
            );
            $updateRequest->execute(['request_id' => $requestId]);

            $pdo->commit();

            $tempPasswordResult = [
                'name' => (string)$row['full_name'],
                'phone' => (string)($row['phone'] ?? ''),
                'email' => (string)($row['email'] ?? ''),
                'password' => $temporaryPassword,
            ];
            $flashSuccess = 'Đã đặt lại mật khẩu. Hãy gọi cho học sinh/phụ huynh và đọc mật khẩu tạm bên dưới.';
            $_SESSION['teacher_csrf_token'] = bin2hex(random_bytes(32));
        } catch (Throwable $e) {
            if ($pdo->inTransaction()) {
                $pdo->rollBack();
            }
            error_log('Teacher reset student password error: ' . $e->getMessage());
            $flashError = $e instanceof RuntimeException
                ? $e->getMessage()
                : 'Không thể đặt lại mật khẩu. Vui lòng thử lại.';
        }
    }
}


// Nạp lại Lớp tôi phụ trách sau POST.
// Khối này phải chạy SAU xử lý add_my_class/remove_my_class vì danh sách ban đầu
// được nạp trước POST, khiến thông báo thành công xuất hiện nhưng UI vẫn báo chưa có lớp.
$myClassAssignments = [];
if ($currentUserRole === 'teacher' && $currentUserId > 0 && $selectedAcademicYearId > 0) {
    try {
        $stmt = $pdo->prepare(
            "SELECT id, academic_year_id, grade, class_name, status
             FROM teacher_class_assignments
             WHERE teacher_id = :teacher_id
               AND school_id = :school_id
               AND academic_year_id = :academic_year_id
               AND status = 'active'
             ORDER BY grade ASC, class_name ASC"
        );
        $stmt->execute([
            'teacher_id' => $currentUserId,
            'school_id' => $teacherScopeSchoolId,
            'academic_year_id' => $selectedAcademicYearId,
        ]);
        $myClassAssignments = $stmt->fetchAll(PDO::FETCH_ASSOC);
    } catch (Throwable $e) {
        error_log('Reload teacher class assignments error: ' . $e->getMessage());
    }
}

// Nạp lại danh sách năm học sau POST để năm vừa thêm xuất hiện ngay.
if (esTableExists($pdo, 'academic_years')) {
    $academicYearColumns = esTableColumns($pdo, 'academic_years');
    if ($academicYearLabelColumn === null) {
        foreach (['name', 'year_name', 'label', 'title', 'school_year'] as $candidate) {
            if (isset($academicYearColumns[$candidate])) {
                $academicYearLabelColumn = $candidate;
                break;
            }
        }
    }
    if ($academicYearLabelColumn !== null && isset($academicYearColumns['id'])) {
        $orderSql = isset($academicYearColumns['start_date'])
            ? " ORDER BY start_date DESC, id DESC"
            : " ORDER BY id DESC";
        $academicYearOptions = $pdo->query(
            "SELECT id, `" . $academicYearLabelColumn . "` AS year_label FROM academic_years" . $orderSql
        )->fetchAll(PDO::FETCH_ASSOC);
        if ($selectedAcademicYearId <= 0 && $academicYearOptions !== []) {
            $selectedAcademicYearId = (int)($academicYearOptions[0]['id'] ?? 0);
        }
    }
}

$pendingSql =
    "SELECT
        r.id AS request_id,
        r.requested_identifier,
        r.requested_at,
        s.id AS student_id,
        s.full_name,
        s.phone,
        s.email,
        s.status
     FROM password_reset_requests r
     INNER JOIN students s ON s.id = r.student_id
     WHERE r.status = 'pending'" .
     esTeacherScopeSql($currentUserRole, $teacherScopeReady) . "
     ORDER BY r.requested_at ASC";
$pendingStmt = $pdo->prepare($pendingSql);
$pendingStmt->execute(esTeacherScopeParams(
    $currentUserRole,
    $teacherScopeReady,
    $teacherScopeProvince,
    $teacherScopeSchool
));
$pendingRequests = $pendingStmt->fetchAll(PDO::FETCH_ASSOC);

$studentSelect = ['id','full_name','phone','email','status','created_at'];
if ($hasGrade) $studentSelect[] = 'grade';
if ($hasClassName) $studentSelect[] = 'class_name';

$studentsSql =
    "SELECT " . implode(', ', $studentSelect) . "
     FROM students
     WHERE 1 = 1" . esTeacherScopeSql($currentUserRole, $teacherScopeReady) . "
     ORDER BY status = 'active' DESC, " .
        ($hasGrade ? "grade ASC, " : "") .
        ($hasClassName ? "class_name ASC, " : "") .
        "full_name ASC, id ASC";
$studentsStmt = $pdo->prepare($studentsSql);
$studentsStmt->execute(esTeacherScopeParams(
    $currentUserRole,
    $teacherScopeReady,
    $teacherScopeProvince,
    $teacherScopeSchool
));
$students = $studentsStmt->fetchAll(PDO::FETCH_ASSOC);

$classOptions = [];
$archivedClassOptions = [];
if ($hasClassName) {
    $yearWhere = '';
    $yearParams = [];
    if ($hasAcademicYearId && $selectedAcademicYearId > 0) {
        $yearWhere = " AND academic_year_id = :academic_year_id";
        $yearParams['academic_year_id'] = $selectedAcademicYearId;
    }

    $yearWhere .= esTeacherScopeSql($currentUserRole, $teacherScopeReady);
    $yearParams += esTeacherScopeParams(
        $currentUserRole,
        $teacherScopeReady,
        $teacherScopeProvince,
        $teacherScopeSchool
    );

    $sql = "SELECT class_name" . ($hasGrade ? ", MAX(grade) AS grade" : "") . ", COUNT(*) AS student_count
            FROM students
            WHERE class_name IS NOT NULL
              AND TRIM(class_name) <> ''
              AND status = 'active'" . $yearWhere . "
            GROUP BY class_name
            ORDER BY " . ($hasGrade ? "MAX(grade) ASC, " : "") . "class_name ASC";
    $stmt = $pdo->prepare($sql);
    $stmt->execute($yearParams);
    $classOptions = $stmt->fetchAll(PDO::FETCH_ASSOC);

    $sql = "SELECT class_name" . ($hasGrade ? ", MAX(grade) AS grade" : "") . ", COUNT(*) AS student_count
            FROM students
            WHERE class_name IS NOT NULL
              AND TRIM(class_name) <> ''
              AND status = 'suspended'" . $yearWhere . "
            GROUP BY class_name
            ORDER BY " . ($hasGrade ? "MAX(grade) ASC, " : "") . "class_name ASC";
    $stmt = $pdo->prepare($sql);
    $stmt->execute($yearParams);
    $archivedClassOptions = $stmt->fetchAll(PDO::FETCH_ASSOC);
}

$historyStudents = [];
if ($selectedAcademicYearId > 0) {
    $historySql = "SELECT
            h.student_id,
            h.grade,
            h.class_name,
            s.full_name,
            s.phone,
            s.email,
            s.status,
            s.created_at
        FROM student_class_history h
        INNER JOIN students s ON s.id = h.student_id
        WHERE h.academic_year_id = :history_year_id";
    $historyParams = ['history_year_id' => $selectedAcademicYearId];

    if ($currentUserRole === 'teacher') {
        if (!$teacherScopeReady) {
            $historySql .= " AND 1 = 0";
        } elseif ($teacherScopeProvinceId > 0 && $teacherScopeSchoolId > 0) {
            $historySql .= " AND h.province_id = :history_province_id
                             AND h.school_id = :history_school_id";
            $historyParams['history_province_id'] = $teacherScopeProvinceId;
            $historyParams['history_school_id'] = $teacherScopeSchoolId;
        } else {
            $historySql .= " AND TRIM(h.province) = :history_province
                             AND TRIM(h.school_name) = :history_school";
            $historyParams['history_province'] = $teacherScopeProvince;
            $historyParams['history_school'] = $teacherScopeSchool;
        }
    }

    $historySql .= " ORDER BY h.grade ASC, h.class_name ASC, s.full_name ASC";
    $historyStmt = $pdo->prepare($historySql);
    $historyStmt->execute($historyParams);
    $historyStudents = $historyStmt->fetchAll(PDO::FETCH_ASSOC);
}

$studentFilterClasses = [];
if ($hasClassName) {
    foreach ($students as $studentRow) {
        $className = trim((string)($studentRow['class_name'] ?? ''));
        if ($className !== '') {
            $studentFilterClasses[$className] = true;
        }
    }
    $studentFilterClasses = array_keys($studentFilterClasses);
    natcasesort($studentFilterClasses);
    $studentFilterClasses = array_values($studentFilterClasses);
}

ob_start();
?>
<style>
.students-page{display:grid;gap:22px}
.students-heading{display:flex;align-items:flex-start;justify-content:space-between;gap:16px}
.students-heading h1{margin:0;font-size:26px;color:#10213d}
.students-heading p{margin:6px 0 0;color:#718096;font-size:14px}
.stat-pill{display:inline-flex;align-items:center;gap:8px;padding:9px 13px;border-radius:999px;background:#fff4df;color:#a65b00;font-weight:700;font-size:13px}
.panel{background:#fff;border:1px solid #e6ebf2;border-radius:18px;box-shadow:0 5px 18px rgba(18,38,63,.05);overflow:hidden}
.panel-head{padding:18px 20px;border-bottom:1px solid #edf1f6;display:flex;align-items:center;justify-content:space-between;gap:14px}
.panel-head h2{margin:0;font-size:18px;color:#15233c}
.panel-head p{margin:5px 0 0;font-size:13px;color:#7a8799}
.notice{padding:13px 15px;border-radius:13px;font-size:14px;line-height:1.5}
.notice-success{background:#ecfdf3;color:#166534;border:1px solid #bbf7d0}
.notice-error{background:#fff1f2;color:#be123c;border:1px solid #fecdd3}
.reset-result{padding:18px 20px;background:#f1f7ff;border:1px solid #bfd8ff;border-radius:16px}
.reset-result h3{margin:0 0 12px;color:#124a89;font-size:17px}
.reset-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px}
.result-box{background:#fff;border:1px solid #dbe8f7;border-radius:12px;padding:12px}
.result-box span{display:block;color:#7c8ba0;font-size:12px;margin-bottom:4px}
.result-box strong{font-size:16px;color:#15233c;word-break:break-word}
.result-box.password strong{font-size:22px;letter-spacing:1px;color:#0d67c6}
.table-wrap{overflow:auto}
.student-table{width:100%;border-collapse:collapse;min-width:1040px}
.student-table th,.student-table td{padding:13px 16px;border-bottom:1px solid #edf1f6;text-align:left;font-size:13px;vertical-align:middle}
.student-table th{background:#f8fafc;color:#617085;font-weight:700}
.student-table td{color:#28384f}
.student-table tr:last-child td{border-bottom:0}
.student-name{font-weight:750;color:#14243c}
.muted{color:#8491a4}
.badge{display:inline-flex;align-items:center;padding:5px 9px;border-radius:999px;font-size:11px;font-weight:750}
.badge-pending{background:#fff4dd;color:#9a5a00}
.badge-active{background:#e9fbef;color:#26733b}
.badge-inactive{background:#f1f3f5;color:#68717d}
.reset-btn{border:0;border-radius:10px;background:#2563eb;color:#fff;font-weight:750;padding:9px 12px;cursor:pointer;white-space:nowrap}
.reset-btn:hover{background:#1d4ed8}
.row-action-btn{border:1px solid #dbe3ee;border-radius:10px;background:#fff;color:#475569;font-weight:750;padding:9px 10px;cursor:pointer;white-space:nowrap}
.row-action-btn:hover{background:#f8fafc}.row-action-btn.danger{border-color:#fecaca;color:#b91c1c;background:#fff7f7}.row-action-btn.danger:hover{background:#fee2e2}
.year-selector{display:flex;align-items:end;gap:12px;flex-wrap:wrap;padding:14px 20px;border-bottom:1px solid #edf1f6;background:#f8fbff}
.year-selector label{display:grid;gap:5px;color:#617085;font-size:12px;font-weight:700}
.year-selector select{min-width:220px;min-height:40px;border:1px solid #dbe3ee;border-radius:10px;padding:8px 10px;background:#fff;font:inherit}
.year-note{font-size:12.5px;color:#64748b;line-height:1.45}
.bulk-promote-box{grid-column:1/-1;border:1px solid #dbe7f6;border-radius:14px;padding:15px;background:#f8fbff}
.bulk-promote-box h3{margin:0 0 6px;color:#17324d;font-size:16px}
.bulk-promote-box p{margin:0 0 12px;color:#748398;font-size:12.5px;line-height:1.45}
.bulk-promote-wrap{overflow:auto}
.bulk-promote-table{width:100%;border-collapse:collapse;min-width:760px}
.bulk-promote-table th,.bulk-promote-table td{padding:10px 9px;border-bottom:1px solid #e7edf5;text-align:left;font-size:12.5px;vertical-align:middle}
.bulk-promote-table th{background:#f1f6fc;color:#5f6f84;font-weight:800}
.bulk-promote-table tr:last-child td{border-bottom:0}
.bulk-promote-table select,.bulk-promote-table input[type=text]{width:100%;min-height:38px;border:1px solid #dbe3ee;border-radius:9px;padding:7px 9px;background:#fff;font:inherit}
.bulk-promote-check{width:18px;height:18px}
.bulk-promote-actions{display:flex;justify-content:space-between;align-items:center;gap:10px;flex-wrap:wrap;margin-top:12px}
.bulk-promote-count{font-size:12.5px;color:#53657b;font-weight:750}
.year-tools{display:grid;grid-template-columns:1fr 1fr;gap:14px;padding:18px 20px}
.tool-box{border:1px solid #e4eaf2;border-radius:14px;padding:15px;background:#fbfdff}
.tool-box h3{margin:0 0 6px;color:#17324d;font-size:16px}
.tool-box p{margin:0 0 12px;color:#748398;font-size:12.5px;line-height:1.45}
.tool-grid{display:grid;grid-template-columns:1fr 1fr;gap:9px}
.tool-grid .full{grid-column:1/-1}
.tool-box select,.tool-box input{width:100%;min-height:40px;border:1px solid #dbe3ee;border-radius:10px;padding:8px 10px;background:#fff;font:inherit}
.tool-actions{display:flex;gap:8px;margin-top:10px;flex-wrap:wrap}
.btn-promote,.btn-archive,.btn-restore{border:0;border-radius:10px;padding:9px 11px;font-weight:800;cursor:pointer;white-space:nowrap}
.btn-promote{background:#2563eb;color:#fff}
.btn-archive{background:#fff2e8;color:#b45309;border:1px solid #fed7aa}
.btn-restore{background:#ecfdf3;color:#166534;border:1px solid #bbf7d0}
.student-actions{display:flex;gap:6px;align-items:center}
.student-actions form{margin:0}
.bulk-create-box{margin:18px 20px;border:1px solid #dce7f4;border-radius:14px;background:#fbfdff;overflow:hidden}
.bulk-create-head{padding:15px 16px;border-bottom:1px solid #e8eef5}
.bulk-create-head h3{margin:0 0 5px;color:#17324d;font-size:16px}
.bulk-create-head p{margin:0;color:#748398;font-size:12.5px;line-height:1.45}
.bulk-create-body{padding:15px 16px}
.bulk-create-body textarea{width:100%;min-height:150px;resize:vertical;border:1px solid #dbe3ee;border-radius:11px;padding:11px 12px;background:#fff;font:inherit;line-height:1.5}
.bulk-create-help{margin-top:8px;color:#64748b;font-size:12px;line-height:1.45}
.bulk-create-actions{display:flex;align-items:center;justify-content:space-between;gap:10px;flex-wrap:wrap;margin-top:10px}
.bulk-create-result{margin:0 20px 18px;border:1px solid #dbe7f2;border-radius:12px;overflow:auto}
.bulk-create-result table{width:100%;border-collapse:collapse;min-width:680px}
.bulk-create-result th,.bulk-create-result td{padding:10px 12px;border-bottom:1px solid #edf1f6;text-align:left;font-size:12.5px}
.bulk-create-result th{background:#f8fafc;color:#617085}
.bulk-create-result tr:last-child td{border-bottom:0}
.bulk-ok{color:#166534;font-weight:800}
.bulk-skip{color:#9a3412;font-weight:700}
.password-cell{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-weight:800;color:#0d67c6}
.filter-bar{display:flex;align-items:end;gap:10px;flex-wrap:wrap;padding:14px 20px;border-bottom:1px solid #edf1f6;background:#fff}
.filter-bar label{display:grid;gap:5px;color:#617085;font-size:12px;font-weight:700}
.filter-bar input,.filter-bar select{min-height:40px;border:1px solid #dbe3ee;border-radius:10px;padding:8px 10px;background:#fff;font:inherit;outline:none}
.filter-bar input{min-width:260px}
.filter-bar select{min-width:130px}
.filter-bar input:focus,.filter-bar select:focus{border-color:#80aef8;box-shadow:0 0 0 3px rgba(37,99,235,.10)}
.filter-caption{width:100%;font-size:12.5px;font-weight:800;color:#334155;margin-bottom:-2px}
.assign-bar{display:flex;align-items:end;gap:10px;flex-wrap:wrap;padding:14px 20px;border-bottom:1px solid #edf1f6;background:#f8fbff}
.assign-bar label{display:grid;gap:5px;color:#617085;font-size:12px;font-weight:700}
.assign-bar select,.assign-bar input{min-height:40px;border:1px solid #dbe3ee;border-radius:10px;padding:8px 10px;background:#fff;font:inherit}
.assign-bar select{min-width:120px}
.assign-bar input{min-width:190px}
.assign-caption{width:100%;font-size:12.5px;font-weight:800;color:#1e3a5f;margin-bottom:-2px}
.assign-count{align-self:center;margin-left:auto;color:#53657b;font-size:12.5px;font-weight:750}
.assign-btn{min-height:40px;border:0;border-radius:10px;padding:9px 13px;background:#2563eb;color:#fff;font-weight:800;cursor:pointer}
.student-check{width:18px;height:18px;cursor:pointer}
@media(max-width:900px){.year-tools{grid-template-columns:1fr}.assign-count{margin-left:0;width:100%}.filter-bar input{min-width:180px}}
.empty-state{padding:28px 20px;text-align:center;color:#7b8899}
@media(max-width:800px){.reset-grid{grid-template-columns:1fr}.students-heading{flex-direction:column}}

.students-tabs{display:flex;gap:8px;flex-wrap:wrap;padding:6px;background:#eef4fb;border:1px solid #dbe5f1;border-radius:14px}
.students-tabs a{display:inline-flex;align-items:center;gap:7px;padding:10px 14px;border-radius:10px;color:#52647b;text-decoration:none;font-weight:800;font-size:13px}
.students-tabs a:hover{background:#fff;color:#1d4ed8}
.students-tabs a.active{background:#fff;color:#1d4ed8;box-shadow:0 2px 8px rgba(30,64,175,.10)}
@media(max-width:700px){.students-tabs{display:grid;grid-template-columns:1fr}.students-tabs a{justify-content:center}}


.my-classes-card{background:#fff;border:1px solid #e5eaf1;border-radius:16px;padding:18px;margin:0 0 18px;box-shadow:0 6px 20px rgba(15,23,42,.04)}
.my-classes-head{display:flex;justify-content:space-between;gap:14px;align-items:flex-start;margin-bottom:14px}
.my-classes-head h3{margin:0;font-size:17px;color:#172033}
.my-classes-head p{margin:5px 0 0;color:#748398;font-size:12.5px}
.my-class-form{display:grid;grid-template-columns:180px 1fr auto;gap:10px;align-items:end;margin-bottom:14px}
.my-class-form label{display:grid;gap:6px;font-size:12px;font-weight:700;color:#52647b}
.my-class-form select,.my-class-form input{height:40px;border:1px solid #d8e0ea;border-radius:9px;padding:0 10px;background:#fff}
.my-class-list{display:flex;flex-wrap:wrap;gap:8px}
.my-class-chip{display:flex;align-items:center;gap:7px;border:1px solid #d9e8ff;background:#f4f8ff;border-radius:10px;padding:7px 9px;color:#244b7a;font-size:12px;font-weight:800}
.my-class-chip form{margin:0}
.my-class-remove{border:0;background:transparent;color:#9aa8ba;cursor:pointer;font-weight:900;padding:0 2px}
.my-class-empty{padding:10px 12px;border-radius:10px;background:#f8fafc;color:#7b8798;font-size:12.5px}
@media(max-width:760px){.my-class-form{grid-template-columns:1fr}.my-classes-head{display:block}}

</style>

<div class="students-page">
    <div class="students-heading">
        <div>
            <h1>Students</h1>
            <p>Quản lý học sinh và xử lý yêu cầu quên mật khẩu.</p>
        </div>
        <div class="stat-pill">
            🔑 <?= count($pendingRequests) ?> yêu cầu chờ xử lý
        </div>
    </div>


    <nav class="students-tabs" aria-label="Quản lý học sinh">
        <a class="" href="index.php">👥 Học sinh</a>
        <a class="active" href="academic-years.php">🎓 Năm học &amp; Lớp</a>
        <a class="" href="import.php">📥 Import học sinh</a>
    </nav>

    <?php if ($flashError !== ''): ?>
        <div class="notice notice-error">
            <?= htmlspecialchars($flashError, ENT_QUOTES, 'UTF-8') ?>
        </div>
    <?php endif; ?>

    <?php if ($flashSuccess !== ''): ?>
        <div class="notice notice-success">
            <?= htmlspecialchars($flashSuccess, ENT_QUOTES, 'UTF-8') ?>
        </div>
    <?php endif; ?>

    <?php if ($tempPasswordResult !== null): ?>
        <div class="reset-result">
            <h3>☎️ Thông tin để giáo viên liên hệ</h3>
            <div class="reset-grid">
                <div class="result-box">
                    <span>Học sinh</span>
                    <strong><?= htmlspecialchars($tempPasswordResult['name'], ENT_QUOTES, 'UTF-8') ?></strong>
                </div>
                <div class="result-box">
                    <span>Số điện thoại</span>
                    <strong><?= htmlspecialchars($tempPasswordResult['phone'] ?: 'Chưa có', ENT_QUOTES, 'UTF-8') ?></strong>
                </div>
                <div class="result-box password">
                    <span>Mật khẩu tạm</span>
                    <strong><?= htmlspecialchars($tempPasswordResult['password'], ENT_QUOTES, 'UTF-8') ?></strong>
                </div>
            </div>
            <p style="margin:12px 0 0;color:#59697d;font-size:13px;">
                Mật khẩu tạm chỉ hiện trong lần reset này. Giáo viên gọi số điện thoại trên và đọc mật khẩu cho học sinh/phụ huynh.
            </p>
        </div>
    <?php endif; ?>

    <?php if ($currentUserRole === 'teacher' && $teacherScopeReady): ?>
    <div style="padding:10px 12px;border:1px solid #dbeafe;border-radius:10px;background:#eff6ff;color:#1e40af;font-size:12.5px;">
        🏫 <strong><?= htmlspecialchars($teacherScopeSchool, ENT_QUOTES, 'UTF-8') ?></strong>
        <span style="color:#64748b;">• <?= htmlspecialchars($teacherScopeProvince, ENT_QUOTES, 'UTF-8') ?></span>
    </div>
    <?php endif; ?>

    <section class="panel">
        <div class="panel-head">
            <div>
                <h2>🎓 Quản lý năm học</h2>
                <p>Lên lớp mà không làm mất XP, tiến độ và lịch sử học.</p>
            </div>
        </div>


        <?php if ($currentUserRole === 'teacher'): ?>
        <section class="my-classes-card">
            <div class="my-classes-head">
                <div>
                    <h3>👩‍🏫 Lớp tôi phụ trách</h3>
                    <p>
                        Tự khai báo các lớp bạn dạy trong năm học đang chọn.
                        Lớp được gắn tự động với trường của tài khoản Teacher.
                    </p>
                </div>
            </div>

            <form method="post" class="my-class-form">
                <input type="hidden" name="csrf_token" value="<?= htmlspecialchars((string)$_SESSION['teacher_csrf_token'], ENT_QUOTES, 'UTF-8') ?>">
                <input type="hidden" name="action" value="add_my_class">
                <input type="hidden" name="academic_year_id" value="<?= (int)$selectedAcademicYearId ?>">

                <label>
                    Khối
                    <select name="my_class_grade" required>
                        <?php for ($g = 1; $g <= 5; $g++): ?>
                            <option value="<?= $g ?>">Khối <?= $g ?></option>
                        <?php endfor; ?>
                    </select>
                </label>

                <label>
                    Lớp
                    <input type="text" name="my_class_name" maxlength="100" placeholder="Ví dụ: 4/1" required>
                </label>

                <button type="submit" class="btn btn-primary">+ Thêm lớp</button>
            </form>

            <?php if ($selectedAcademicYearId <= 0): ?>
                <div class="my-class-empty">Hãy tạo/chọn Năm học trước khi khai báo lớp phụ trách.</div>
            <?php elseif ($myClassAssignments === []): ?>
                <div class="my-class-empty">Chưa khai báo lớp phụ trách trong năm học này.</div>
            <?php else: ?>
                <div class="my-class-list">
                    <?php foreach ($myClassAssignments as $assignment): ?>
                        <div class="my-class-chip">
                            <span>Khối <?= (int)$assignment['grade'] ?> • <?= htmlspecialchars((string)$assignment['class_name'], ENT_QUOTES, 'UTF-8') ?></span>
                            <form method="post" onsubmit="return confirm('Bỏ lớp này khỏi Lớp tôi phụ trách? Dữ liệu học sinh vẫn được giữ nguyên.');">
                                <input type="hidden" name="csrf_token" value="<?= htmlspecialchars((string)$_SESSION['teacher_csrf_token'], ENT_QUOTES, 'UTF-8') ?>">
                                <input type="hidden" name="action" value="remove_my_class">
                                <input type="hidden" name="assignment_id" value="<?= (int)$assignment['id'] ?>">
                                <button type="submit" class="my-class-remove" title="Bỏ lớp">×</button>
                            </form>
                        </div>
                    <?php endforeach; ?>
                </div>
            <?php endif; ?>
        </section>
        <?php endif; ?>

        <?php if (!$hasClassName): ?>
            <div class="empty-state">Bảng students chưa có cột <strong>class_name</strong>, nên chưa thể quản lý theo lớp.</div>
        <?php else: ?>
            <div class="year-selector">
                <?php if ($academicYearOptions !== []): ?>
                    <form method="get">
                        <label>
                            Năm học
                            <select name="academic_year_id" onchange="this.form.submit()">
                                <?php foreach ($academicYearOptions as $year): ?>
                                    <option value="<?= (int)$year['id'] ?>" <?= (int)$year['id'] === $selectedAcademicYearId ? 'selected' : '' ?>>
                                        <?= htmlspecialchars((string)$year['year_label'], ENT_QUOTES, 'UTF-8') ?>
                                    </option>
                                <?php endforeach; ?>
                            </select>
                        </label>
                    </form>
                    <?php if (!$hasAcademicYearId): ?>
                        <div class="year-note">
                            Hệ thống đã có danh sách năm học, nhưng bảng <strong>students</strong> chưa có
                            <strong>academic_year_id</strong>. Vì vậy hiện chưa thể tách chính xác danh sách lớp theo từng năm học.
                            Các thao tác bên dưới vẫn đang áp dụng cho lớp hiện tại.
                        </div>
                    <?php else: ?>
                        <div class="year-note">Đang xem và thao tác các lớp của năm học đã chọn.</div>
                    <?php endif; ?>

                    <?php if ($currentUserRole === 'admin'): ?>
                        <div style="display:flex;align-items:end;gap:8px;flex-wrap:wrap;margin-left:auto;">
                            <form method="post" style="display:flex;align-items:end;gap:8px;flex-wrap:wrap;">
                                <input type="hidden" name="csrf_token" value="<?= htmlspecialchars((string)$_SESSION['teacher_csrf_token'], ENT_QUOTES, 'UTF-8') ?>">
                                <input type="hidden" name="action" value="add_academic_year">
                                <label>
                                    Thêm năm học
                                    <input
                                        type="text"
                                        name="year_label"
                                        inputmode="numeric"
                                        placeholder="Ví dụ 2027-2028"
                                        pattern="[0-9]{4}-[0-9]{4}"
                                        maxlength="9"
                                        required
                                        style="min-width:160px;min-height:40px;border:1px solid #dbe3ee;border-radius:10px;padding:8px 10px;background:#fff;font:inherit;"
                                    >
                                </label>
                                <button type="submit" class="btn-promote" style="min-height:40px;">＋ Thêm năm học</button>
                            </form>

                            <?php if ($selectedAcademicYearId > 0): ?>
                            <form method="post" onsubmit="return confirm('Xóa năm học đang chọn? Chỉ năm học chưa có dữ liệu mới xóa được.');">
                                <input type="hidden" name="csrf_token" value="<?= htmlspecialchars((string)$_SESSION['teacher_csrf_token'], ENT_QUOTES, 'UTF-8') ?>">
                                <input type="hidden" name="action" value="delete_academic_year">
                                <input type="hidden" name="academic_year_id" value="<?= (int)$selectedAcademicYearId ?>">
                                <button type="submit" style="min-height:40px;border:1px solid #fecaca;border-radius:10px;padding:8px 12px;background:#fff5f5;color:#b91c1c;font:inherit;font-weight:800;cursor:pointer;">🗑 Xóa năm học</button>
                            </form>
                            <?php endif; ?>
                        </div>
                    <?php endif; ?>
                <?php else: ?>
                    <div class="year-note">
                        Chưa đọc được danh sách năm học từ bảng <strong>academic_years</strong>.
                    </div>
                <?php endif; ?>
            </div>

            <div style="padding:16px 20px;border-bottom:1px solid #edf1f6;background:#fff;">
                <div style="display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:10px;">
                    <div>
                        <h3 style="margin:0;color:#17324d;font-size:16px;">📚 Danh sách học sinh năm học đã chọn</h3>
                        <p style="margin:5px 0 0;color:#748398;font-size:12.5px;">Đổi Năm học phía trên để xem lại học sinh của các năm trước.</p>
                    </div>
                    <span class="stat-pill" id="historyStudentCount"><?= count($historyStudents) ?> học sinh</span>
                </div>

                <?php if ($historyStudents !== []): ?>
                    <div style="display:flex;align-items:end;gap:10px;flex-wrap:wrap;margin:12px 0 14px;padding:12px;background:#f8fbff;border:1px solid #e4edf8;border-radius:12px;">
                        <label style="display:grid;gap:5px;color:#617085;font-size:12px;font-weight:700;">
                            Tìm học sinh
                            <input id="historyStudentSearch" type="search" placeholder="Tên, SĐT, Gmail..." style="min-width:220px;min-height:40px;border:1px solid #dbe3ee;border-radius:10px;padding:8px 10px;background:#fff;font:inherit;">
                        </label>
                        <label style="display:grid;gap:5px;color:#617085;font-size:12px;font-weight:700;">
                            Khối
                            <select id="historyGradeFilter" style="min-width:150px;min-height:40px;border:1px solid #dbe3ee;border-radius:10px;padding:8px 10px;background:#fff;font:inherit;">
                                <option value="">Tất cả khối</option>
                                <?php for ($g=1; $g<=5; $g++): ?>
                                    <option value="<?= $g ?>">Khối <?= $g ?></option>
                                <?php endfor; ?>
                            </select>
                        </label>
                        <label style="display:grid;gap:5px;color:#617085;font-size:12px;font-weight:700;">
                            Lớp
                            <select id="historyClassFilter" style="min-width:170px;min-height:40px;border:1px solid #dbe3ee;border-radius:10px;padding:8px 10px;background:#fff;font:inherit;">
                                <option value="">Tất cả lớp</option>
                            </select>
                        </label>
                        <label style="display:grid;gap:5px;color:#617085;font-size:12px;font-weight:700;">
                            Trạng thái
                            <select id="historyStatusFilter" style="min-width:160px;min-height:40px;border:1px solid #dbe3ee;border-radius:10px;padding:8px 10px;background:#fff;font:inherit;">
                                <option value="">Tất cả trạng thái</option>
                                <option value="active">Hoạt động</option>
                                <option value="suspended">Lưu trữ</option>
                            </select>
                        </label>
                        <button type="button" id="historyClearFilter" style="min-height:40px;border:1px solid #dbe3ee;border-radius:10px;padding:8px 14px;background:#fff;color:#475569;font-weight:700;cursor:pointer;">Xóa lọc</button>
                    </div>
                <?php endif; ?>

                <?php if ($historyStudents === []): ?>
                    <div class="empty-state" style="padding:12px 0;">Chưa có dữ liệu học sinh cho năm học này.</div>
                <?php else: ?>
                    <div class="table-wrap">
                        <table class="student-table" id="historyStudentsTable" style="min-width:1180px;">
                            <thead>
                                <tr>
                                    <th>Học sinh</th>
                                    <th>Khối</th>
                                    <th>Lớp</th>
                                    <th>SĐT</th>
                                    <th>Gmail</th>
                                    <th>Trạng thái</th>
                                    <th>Ngày đăng ký</th>
                                    <th>Thao tác</th>
                                </tr>
                            </thead>
                            <tbody>
                            <?php foreach ($historyStudents as $historyStudent): ?>
                                <?php $historyStatus = (string)($historyStudent['status'] ?? 'active'); ?>
                                <tr data-grade="<?= (int)($historyStudent['grade'] ?? 0) ?>" data-class="<?= htmlspecialchars(trim((string)($historyStudent['class_name'] ?? '')), ENT_QUOTES, 'UTF-8') ?>" data-status="<?= htmlspecialchars($historyStatus, ENT_QUOTES, 'UTF-8') ?>">
                                    <td><strong><?= htmlspecialchars((string)$historyStudent['full_name'], ENT_QUOTES, 'UTF-8') ?></strong></td>
                                    <td><?= (int)($historyStudent['grade'] ?? 0) > 0 ? 'Khối '.(int)$historyStudent['grade'] : '—' ?></td>
                                    <td><?= htmlspecialchars((string)($historyStudent['class_name'] ?? ''), ENT_QUOTES, 'UTF-8') ?: '—' ?></td>
                                    <td><?= htmlspecialchars((string)($historyStudent['phone'] ?? ''), ENT_QUOTES, 'UTF-8') ?: '—' ?></td>
                                    <td><?= htmlspecialchars((string)($historyStudent['email'] ?? ''), ENT_QUOTES, 'UTF-8') ?: '—' ?></td>
                                    <td><span class="badge <?= $historyStatus === 'active' ? 'badge-active' : 'badge-inactive' ?>"><?= $historyStatus === 'active' ? 'Hoạt động' : 'Lưu trữ' ?></span></td>
                                    <td><?= !empty($historyStudent['created_at']) ? date('d/m/Y', strtotime((string)$historyStudent['created_at'])) : '—' ?></td>
                                    <td>
                                        <div style="display:flex;gap:6px;flex-wrap:wrap;min-width:220px;">
                                            <form method="post" onsubmit="return confirm('Reset mật khẩu học sinh về 12345678?');">
                                                <input type="hidden" name="csrf_token" value="<?= htmlspecialchars((string)$_SESSION['teacher_csrf_token'], ENT_QUOTES, 'UTF-8') ?>">
                                                <input type="hidden" name="action" value="reset_student_password_fixed">
                                                <input type="hidden" name="student_id" value="<?= (int)$historyStudent['student_id'] ?>">
                                                <button type="submit" class="reset-btn">Reset MK</button>
                                            </form>
                                            <form method="post" onsubmit="return confirm('<?= $historyStatus === 'active' ? 'Lưu trữ học sinh này? XP, tiến độ và lịch sử học vẫn được giữ nguyên.' : 'Kích hoạt lại học sinh này?' ?>');">
                                                <input type="hidden" name="csrf_token" value="<?= htmlspecialchars((string)$_SESSION['teacher_csrf_token'], ENT_QUOTES, 'UTF-8') ?>">
                                                <input type="hidden" name="action" value="<?= $historyStatus === 'active' ? 'archive_student' : 'restore_student' ?>">
                                                <input type="hidden" name="student_id" value="<?= (int)$historyStudent['student_id'] ?>">
                                                <button type="submit" class="row-action-btn"><?= $historyStatus === 'active' ? 'Lưu trữ' : 'Kích hoạt' ?></button>
                                            </form>
                                            <?php if ($currentUserRole === 'admin'): ?>
                                            <form method="post" onsubmit="return confirm('Xóa tài khoản học sinh này? Chỉ học sinh chưa có dữ liệu học mới xóa được.');">
                                                <input type="hidden" name="csrf_token" value="<?= htmlspecialchars((string)$_SESSION['teacher_csrf_token'], ENT_QUOTES, 'UTF-8') ?>">
                                                <input type="hidden" name="action" value="delete_student">
                                                <input type="hidden" name="student_id" value="<?= (int)$historyStudent['student_id'] ?>">
                                                <button type="submit" class="row-action-btn danger">Xóa</button>
                                            </form>
                                            <?php endif; ?>
                                        </div>
                                    </td>
                                </tr>
                            <?php endforeach; ?>
                            </tbody>
                        </table>
                    </div>
                <?php endif; ?>
            </div>

            <div class="year-tools">
                <div class="bulk-promote-box">
                    <h3>⬆️ Lên lớp hàng loạt</h3>
                    <p>Chọn nhiều lớp, Năm học mới, Khối mới và Lớp mới. Hệ thống lưu lại lớp của năm cũ trước khi chuyển.</p>

                    <?php if ($classOptions === []): ?>
                        <div class="empty-state" style="padding:10px 0">Chưa có lớp đang hoạt động để lên lớp hàng loạt.</div>
                    <?php elseif (!$hasGrade): ?>
                        <div class="empty-state" style="padding:10px 0">Bảng students chưa có cột <strong>grade</strong>.</div>
                    <?php else: ?>
                    <form method="post" id="bulkPromoteForm" onsubmit="return confirmBulkPromote();">
                        <input type="hidden" name="csrf_token" value="<?= htmlspecialchars((string)$_SESSION['teacher_csrf_token'], ENT_QUOTES, 'UTF-8') ?>">
                        <input type="hidden" name="academic_year_id" value="<?= $selectedAcademicYearId ?>">
                        <input type="hidden" name="action" value="bulk_promote_classes">

                        <?php if ($hasAcademicYearId && $academicYearOptions !== []): ?>
                            <div style="max-width:320px;margin:0 0 12px;">
                                <label style="display:grid;gap:5px;color:#617085;font-size:12px;font-weight:700;">
                                    Năm học mới
                                    <select name="target_academic_year_id" required style="min-height:40px;border:1px solid #dbe3ee;border-radius:10px;padding:8px 10px;background:#fff;">
                                        <option value="">Chọn năm học mới</option>
                                        <?php foreach ($academicYearOptions as $year): ?>
                                            <?php if ((int)$year['id'] !== $selectedAcademicYearId): ?>
                                                <option value="<?= (int)$year['id'] ?>"><?= htmlspecialchars((string)$year['year_label'], ENT_QUOTES, 'UTF-8') ?></option>
                                            <?php endif; ?>
                                        <?php endforeach; ?>
                                    </select>
                                </label>
                            </div>
                        <?php endif; ?>

                        <div class="bulk-promote-wrap">
                            <table class="bulk-promote-table">
                                <thead>
                                <tr>
                                    <th style="width:48px"><input type="checkbox" id="bulkPromoteSelectAll" class="bulk-promote-check" aria-label="Chọn tất cả lớp"></th>
                                    <th>Lớp hiện tại</th>
                                    <th>Số HS</th>
                                    <th>Khối mới</th>
                                    <th>Lớp mới</th>
                                </tr>
                                </thead>
                                <tbody>
                                <?php foreach ($classOptions as $i => $class): ?>
                                <tr>
                                    <td>
                                        <input type="checkbox" class="bulk-promote-check bulk-promote-select" name="bulk_selected[]" value="<?= (int)$i ?>">
                                        <input type="hidden" name="bulk_source_class[<?= (int)$i ?>]" value="<?= htmlspecialchars((string)$class['class_name'], ENT_QUOTES, 'UTF-8') ?>">
                                    </td>
                                    <td>
                                        <strong><?= htmlspecialchars((string)$class['class_name'], ENT_QUOTES, 'UTF-8') ?></strong>
                                        <div class="muted">Khối <?= (int)($class['grade'] ?? 0) ?></div>
                                    </td>
                                    <td><?= (int)$class['student_count'] ?></td>
                                    <td>
                                        <select name="bulk_target_grade[<?= (int)$i ?>]">
                                            <?php for ($g=1;$g<=5;$g++): ?>
                                                <option value="<?= $g ?>" <?= $g === min(5, ((int)($class['grade'] ?? 0)) + 1) ? 'selected' : '' ?>>
                                                    Khối <?= $g ?>
                                                </option>
                                            <?php endfor; ?>
                                        </select>
                                    </td>
                                    <td>
                                        <input type="text"
                                               name="bulk_target_class[<?= (int)$i ?>]"
                                               placeholder="Ví dụ: <?= min(5, ((int)($class['grade'] ?? 0)) + 1) ?>/1"
                                               autocomplete="off">
                                    </td>
                                </tr>
                                <?php endforeach; ?>
                                </tbody>
                            </table>
                        </div>

                        <div class="bulk-promote-actions">
                            <span id="bulkPromoteCount" class="bulk-promote-count">0 lớp được chọn</span>
                            <button type="submit" class="btn-promote">⬆️ Lên lớp đã chọn</button>
                        </div>
                    </form>
                    <?php endif; ?>
                </div>

                <div class="tool-box">
                    <h3>⬆️ Cho cả lớp lên lớp</h3>
                    <p>Chọn năm học mới, khối mới và lớp mới. Giữ nguyên tài khoản, avatar, XP và toàn bộ lịch sử học.</p>
                    <?php if ($classOptions === []): ?>
                        <div class="empty-state" style="padding:10px 0">Chưa có lớp đang hoạt động.</div>
                    <?php elseif ($hasGrade): ?>
                    <form method="post" onsubmit="return confirm('Xác nhận cho toàn bộ học sinh đang hoạt động của lớp này lên lớp mới?');">
                        <input type="hidden" name="csrf_token" value="<?= htmlspecialchars((string)$_SESSION['teacher_csrf_token'], ENT_QUOTES, 'UTF-8') ?>">
                        <input type="hidden" name="academic_year_id" value="<?= $selectedAcademicYearId ?>">
                        <input type="hidden" name="action" value="promote_class">
                        <div class="tool-grid">
                            <label class="full"><select name="source_class" required><option value="">Chọn lớp hiện tại</option><?php foreach ($classOptions as $class): ?><option value="<?= htmlspecialchars((string)$class['class_name'], ENT_QUOTES, 'UTF-8') ?>"><?= htmlspecialchars((string)$class['class_name'], ENT_QUOTES, 'UTF-8') ?> (<?= (int)$class['student_count'] ?> HS • Khối <?= (int)($class['grade'] ?? 0) ?>)</option><?php endforeach; ?></select></label>
                            <?php if ($hasAcademicYearId && $academicYearOptions !== []): ?>
                                <label class="full"><select name="target_academic_year_id" required><option value="">Năm học mới</option><?php foreach ($academicYearOptions as $year): ?><?php if ((int)$year['id'] !== $selectedAcademicYearId): ?><option value="<?= (int)$year['id'] ?>"><?= htmlspecialchars((string)$year['year_label'], ENT_QUOTES, 'UTF-8') ?></option><?php endif; ?><?php endforeach; ?></select></label>
                            <?php endif; ?>
                            <label><select name="target_grade" required><option value="">Khối mới</option><?php for ($g=1;$g<=5;$g++): ?><option value="<?= $g ?>">Khối <?= $g ?></option><?php endfor; ?></select></label>
                            <label><input type="text" name="target_class" placeholder="Lớp mới, ví dụ 2/1" required></label>
                        </div>
                        <div class="tool-actions"><button type="submit" class="btn-promote">⬆️ Lên lớp</button></div>
                    </form>
                    <?php else: ?><div class="empty-state" style="padding:10px 0">Bảng students chưa có cột <strong>grade</strong>.</div><?php endif; ?>
                </div>

                

                
            </div>
        <?php endif; ?>
    </section>


</div>

<script>
(function () {
    const input = document.getElementById('studentSearch');
    const gradeFilter = document.getElementById('studentGradeFilter');
    const classFilter = document.getElementById('studentClassFilter');
    const statusFilter = document.getElementById('studentStatusFilter');
    const rows = Array.from(document.querySelectorAll('#studentsTable tbody tr'));

    function applyStudentFilters() {
        const q = input ? input.value.trim().toLowerCase() : '';
        const grade = gradeFilter ? gradeFilter.value : '';
        const className = classFilter ? classFilter.value : '';
        const status = statusFilter ? statusFilter.value : '';

        rows.forEach(function (row) {
            const rowText = row.textContent.toLowerCase();
            const rowGrade = (row.dataset.grade || '').trim();
            const rowClass = (row.dataset.class || '').trim();
            const rowStatus = (row.dataset.status || '').trim();

            const matchSearch = q === '' || rowText.includes(q);
            const matchGrade = grade === '' || rowGrade === grade;
            const matchClass = className === ''
                || (className === '__none__' ? rowClass === '' : rowClass === className);
            const matchStatus = status === '' || rowStatus === status;

            row.style.display = (matchSearch && matchGrade && matchClass && matchStatus) ? '' : 'none';
        });

        refreshCount();
    }

    if (input) input.addEventListener('input', applyStudentFilters);
    if (gradeFilter) gradeFilter.addEventListener('change', applyStudentFilters);
    if (classFilter) classFilter.addEventListener('change', applyStudentFilters);
    if (statusFilter) statusFilter.addEventListener('change', applyStudentFilters);

    const selectAll = document.getElementById('selectAllStudents');
    const boxes = Array.from(document.querySelectorAll('.student-select'));
    const countEl = document.getElementById('assignSelectedCount');

    function refreshCount() {
        const count = boxes.filter(box => box.checked).length;
        const visibleBoxes = boxes.filter(function (box) {
            const row = box.closest('tr');
            return row && row.style.display !== 'none';
        });
        const visibleChecked = visibleBoxes.filter(box => box.checked).length;

        if (countEl) countEl.textContent = count + ' học sinh được chọn';
        if (selectAll) {
            selectAll.checked = visibleBoxes.length > 0 && visibleChecked === visibleBoxes.length;
            selectAll.indeterminate = visibleChecked > 0 && visibleChecked < visibleBoxes.length;
        }
    }

    if (selectAll) {
        selectAll.addEventListener('change', function () {
            boxes.forEach(function (box) {
                const row = box.closest('tr');
                if (!row || row.style.display !== 'none') {
                    box.checked = selectAll.checked;
                }
            });
            refreshCount();
        });
    }

    boxes.forEach(box => box.addEventListener('change', refreshCount));
    applyStudentFilters();

    // Bộ lọc danh sách học sinh theo năm học: Tìm kiếm -> Khối -> Lớp.
    const historySearch = document.getElementById('historyStudentSearch');
    const historyGrade = document.getElementById('historyGradeFilter');
    const historyClass = document.getElementById('historyClassFilter');
    const historyStatus = document.getElementById('historyStatusFilter');
    const historyClear = document.getElementById('historyClearFilter');
    const historyCount = document.getElementById('historyStudentCount');
    const historyRows = Array.from(document.querySelectorAll('#historyStudentsTable tbody tr'));

    function rebuildHistoryClasses() {
        if (!historyClass) return;
        const selectedGrade = historyGrade ? historyGrade.value : '';
        const previous = historyClass.value;
        const classes = [];
        historyRows.forEach(function (row) {
            const rowGrade = (row.dataset.grade || '').trim();
            const rowClass = (row.dataset.class || '').trim();
            if (rowClass && (selectedGrade === '' || rowGrade === selectedGrade) && !classes.includes(rowClass)) {
                classes.push(rowClass);
            }
        });
        classes.sort(function (a, b) { return a.localeCompare(b, 'vi', {numeric:true, sensitivity:'base'}); });
        historyClass.innerHTML = '<option value="">Tất cả lớp</option>';
        classes.forEach(function (className) {
            const option = document.createElement('option');
            option.value = className;
            option.textContent = className;
            historyClass.appendChild(option);
        });
        if (classes.includes(previous)) historyClass.value = previous;
    }

    function applyHistoryFilters() {
        const q = historySearch ? historySearch.value.trim().toLowerCase() : '';
        const grade = historyGrade ? historyGrade.value : '';
        const className = historyClass ? historyClass.value : '';
        const status = historyStatus ? historyStatus.value : '';
        let visible = 0;
        historyRows.forEach(function (row) {
            const matchSearch = q === '' || row.textContent.toLowerCase().includes(q);
            const matchGrade = grade === '' || (row.dataset.grade || '') === grade;
            const matchClass = className === '' || (row.dataset.class || '') === className;
            const matchStatus = status === '' || (row.dataset.status || '') === status;
            const show = matchSearch && matchGrade && matchClass && matchStatus;
            row.style.display = show ? '' : 'none';
            if (show) visible++;
        });
        if (historyCount) historyCount.textContent = visible + ' học sinh';
    }

    if (historySearch) historySearch.addEventListener('input', applyHistoryFilters);
    if (historyGrade) historyGrade.addEventListener('change', function () {
        rebuildHistoryClasses();
        if (historyClass) historyClass.value = '';
        applyHistoryFilters();
    });
    if (historyClass) historyClass.addEventListener('change', applyHistoryFilters);
    if (historyStatus) historyStatus.addEventListener('change', applyHistoryFilters);
    if (historyClear) historyClear.addEventListener('click', function () {
        if (historySearch) historySearch.value = '';
        if (historyGrade) historyGrade.value = '';
        rebuildHistoryClasses();
        if (historyClass) historyClass.value = '';
        applyHistoryFilters();
    });
    rebuildHistoryClasses();
    applyHistoryFilters();

    const bulkSelectAll = document.getElementById('bulkPromoteSelectAll');
    const bulkBoxes = Array.from(document.querySelectorAll('.bulk-promote-select'));
    const bulkCountEl = document.getElementById('bulkPromoteCount');

    function refreshBulkPromoteCount() {
        const count = bulkBoxes.filter(box => box.checked).length;
        if (bulkCountEl) bulkCountEl.textContent = count + ' lớp được chọn';
        if (bulkSelectAll) {
            bulkSelectAll.checked = bulkBoxes.length > 0 && bulkBoxes.every(box => box.checked);
            bulkSelectAll.indeterminate = count > 0 && count < bulkBoxes.length;
        }
    }

    if (bulkSelectAll) {
        bulkSelectAll.addEventListener('change', function () {
            bulkBoxes.forEach(box => box.checked = bulkSelectAll.checked);
            refreshBulkPromoteCount();
        });
    }
    bulkBoxes.forEach(box => box.addEventListener('change', refreshBulkPromoteCount));
    refreshBulkPromoteCount();

    window.confirmBulkPromote = function () {
        const selected = bulkBoxes.filter(box => box.checked);
        if (selected.length < 1) {
            alert('Hãy chọn ít nhất một lớp cần lên lớp.');
            return false;
        }

        for (const box of selected) {
            const row = box.closest('tr');
            const targetClass = row ? row.querySelector('input[type="text"]') : null;
            if (!targetClass || targetClass.value.trim() === '') {
                alert('Hãy nhập lớp mới cho tất cả các lớp đã chọn.');
                if (targetClass) targetClass.focus();
                return false;
            }
        }

        return confirm(
            'Xác nhận lên lớp hàng loạt ' + selected.length +
            ' lớp? XP, tiến độ và lịch sử học của học sinh sẽ được giữ nguyên.'
        );
    };

    window.confirmAssignClass = function () {
        const selected = boxes.filter(box => box.checked).length;
        if (selected < 1) {
            alert('Hãy chọn ít nhất một học sinh.');
            return false;
        }
        return confirm('Gán ' + selected + ' học sinh vào lớp đã chọn? Việc này không ảnh hưởng XP hoặc tiến độ học.');
    };
})();
</script>
<?php
$content = ob_get_clean();
?>
<!DOCTYPE html>
<html lang="vi" data-theme="light">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="theme-color" content="#2563eb">
    <title><?= htmlspecialchars($pageTitle . ' | EnglishSpeak', ENT_QUOTES, 'UTF-8') ?></title>

    <link rel="stylesheet" href="../assets/css/app.css?v=1.1">
    <link rel="stylesheet" href="../assets/css/sidebar.css?v=1.1">
    <link rel="stylesheet" href="../assets/css/topbar.css?v=1.1">
    <link rel="stylesheet" href="../assets/css/cards.css?v=1.1">
    <link rel="stylesheet" href="../assets/css/buttons.css?v=1.1">
    <link rel="stylesheet" href="../assets/css/forms.css?v=1.1">
    <link rel="stylesheet" href="../assets/css/tables.css?v=1.1">
    <link rel="stylesheet" href="../assets/css/utilities.css?v=1.1">
</head>
<body>
<div class="app-shell">
    <?php require __DIR__ . '/../partials/sidebar.php'; ?>
    <div class="sidebar-overlay" id="sidebarOverlay" aria-hidden="true"></div>

    <main class="main-area">
        <?php require __DIR__ . '/../partials/topbar.php'; ?>
        <section class="page-content">
            <?= $content ?>
        </section>
        <?php require __DIR__ . '/../partials/footer.php'; ?>
    </main>
</div>
<script src="../assets/js/app.js?v=1.1"></script>
</body>
</html>
