<?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();

/*
 * Data scope:
 * - admin: unrestricted
 * - teacher: only students in the teacher's province + school_name
 * The scope is loaded from teacher_profiles on every request; do not trust filter inputs.
 */
$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 esTeacherStudentScopeSql(
    string $role,
    bool $scopeReady,
    string $alias = ''
): string {
    global $studentColumns, $teacherScopeProvinceId, $teacherScopeSchoolId;

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

    $prefix = $alias !== '' ? $alias . '.' : '';

    // Ưu tiên khóa ID. Chỉ fallback về tên để tương thích DB cũ.
    if (isset($studentColumns['province_id'], $studentColumns['school_id'])
        && $teacherScopeProvinceId > 0 && $teacherScopeSchoolId > 0) {
        return " AND {$prefix}province_id = :scope_province_id
                 AND {$prefix}school_id = :scope_school_id";
    }

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

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

    if ($role === 'admin' || !$scopeReady) 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ể truy cập dữ liệu học sinh.';
}

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']);

$provinceField = null;
foreach (['province_id', 'province', 'province_name'] as $candidate) {
    if (isset($studentColumns[$candidate])) {
        $provinceField = $candidate;
        break;
    }
}

$schoolField = null;
foreach (['school_id', 'school', 'school_name'] as $candidate) {
    if (isset($studentColumns[$candidate])) {
        $schoolField = $candidate;
        break;
    }
}

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 = 0;
$currentAcademicYearId = 0;
$currentAcademicYearLabel = '';
$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);

        // Thẻ "Học sinh" luôn dùng năm học hiện hành, không nhận năm cũ từ URL.
        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) $currentAcademicYearId = (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) $currentAcademicYearId = (int)$active;
        }

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

        foreach ($academicYearOptions as $yearOption) {
            if ((int)($yearOption['id'] ?? 0) === $currentAcademicYearId) {
                $currentAcademicYearLabel = trim((string)($yearOption['year_label'] ?? ''));
                break;
            }
        }

        $selectedAcademicYearId = $currentAcademicYearId;
    }
}

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

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 === 'create_student_manual') {
        try {
            $fullName = trim((string)($_POST['full_name'] ?? ''));
            $phone = trim((string)($_POST['phone'] ?? ''));
            $email = trim((string)($_POST['email'] ?? ''));
            $grade = $hasGrade ? max(1, min(5, (int)($_POST['grade'] ?? 0))) : null;
            $className = trim((string)($_POST['class_name'] ?? ''));
            $birthDate = trim((string)($_POST['birth_date'] ?? ''));
            $gender = trim((string)($_POST['gender'] ?? ''));
            $postAcademicYearId = max(0, (int)($_POST['academic_year_id'] ?? 0));
            $adminProvinceId = $currentUserRole === 'admin' ? max(0, (int)($_POST['admin_province_id'] ?? 0)) : 0;
            $adminSchoolId = $currentUserRole === 'admin' ? max(0, (int)($_POST['admin_school_id'] ?? 0)) : 0;

            if ($fullName === '') throw new RuntimeException('Hãy nhập họ tên học sinh.');
            if ($phone === '' && $email === '') throw new RuntimeException('Hãy nhập SĐT hoặc Gmail để học sinh đăng nhập.');
            if ($email !== '' && !filter_var($email, FILTER_VALIDATE_EMAIL)) throw new RuntimeException('Gmail không hợp lệ.');
            if ($currentUserRole === 'teacher' && !$teacherScopeReady) throw new RuntimeException('Tài khoản giáo viên chưa có phạm vi Tỉnh/Trường hợp lệ.');

            $adminProvinceLabel = '';
            $adminSchoolLabel = '';
            if ($currentUserRole === 'admin') {
                if ($adminProvinceId <= 0) throw new RuntimeException('Hãy chọn Tỉnh cho học sinh.');
                if ($adminSchoolId <= 0) throw new RuntimeException('Hãy chọn Trường cho học sinh.');

                if (!esTableExists($pdo, 'provinces') || !esTableExists($pdo, 'schools')) {
                    throw new RuntimeException('Chưa có bảng Tỉnh/Trường để gán học sinh.');
                }
                $provinceCols = esTableColumns($pdo, 'provinces');
                $provinceNameCol = null;
                foreach (['name', 'province_name', 'title'] as $candidate) {
                    if (isset($provinceCols[$candidate])) { $provinceNameCol = $candidate; break; }
                }
                $schoolCols = esTableColumns($pdo, 'schools');
                $schoolNameCol = null;
                foreach (['name', 'school_name', 'title'] as $candidate) {
                    if (isset($schoolCols[$candidate])) { $schoolNameCol = $candidate; break; }
                }
                if ($provinceNameCol === null || $schoolNameCol === null) {
                    throw new RuntimeException('Không đọc được tên Tỉnh/Trường từ cơ sở dữ liệu.');
                }

                $pStmt = $pdo->prepare("SELECT `{$provinceNameCol}` FROM provinces WHERE id = :id LIMIT 1");
                $pStmt->execute(['id' => $adminProvinceId]);
                $adminProvinceLabel = trim((string)$pStmt->fetchColumn());
                if ($adminProvinceLabel === '') throw new RuntimeException('Tỉnh đã chọn không hợp lệ.');

                $schoolSql = "SELECT `{$schoolNameCol}` FROM schools WHERE id = :id";
                $schoolParams = ['id' => $adminSchoolId];
                if (isset($schoolCols['province_id'])) {
                    $schoolSql .= " AND province_id = :province_id";
                    $schoolParams['province_id'] = $adminProvinceId;
                }
                $schoolSql .= " LIMIT 1";
                $sStmt = $pdo->prepare($schoolSql);
                $sStmt->execute($schoolParams);
                $adminSchoolLabel = trim((string)$sStmt->fetchColumn());
                if ($adminSchoolLabel === '') throw new RuntimeException('Trường đã chọn không thuộc Tỉnh đã chọn.');
            }

            $duplicateWhere = [];
            $duplicateParams = [];
            if ($phone !== '' && isset($studentColumns['phone'])) { $duplicateWhere[] = 'phone = :manual_phone'; $duplicateParams['manual_phone'] = $phone; }
            if ($email !== '' && isset($studentColumns['email'])) { $duplicateWhere[] = 'email = :manual_email'; $duplicateParams['manual_email'] = $email; }
            if ($duplicateWhere) {
                $check = $pdo->prepare('SELECT id FROM students WHERE ' . implode(' OR ', $duplicateWhere) . ' LIMIT 1');
                $check->execute($duplicateParams);
                if ($check->fetchColumn()) throw new RuntimeException('SĐT hoặc Gmail đã tồn tại trong hệ thống.');
            }

            $temporaryPassword = '12345678';
            $insertCols = []; $insertMarks = []; $insertParams = [];
            $addManualField = static function(string $col, mixed $value) use (&$insertCols, &$insertMarks, &$insertParams, $studentColumns): void {
                if (!isset($studentColumns[$col])) return;
                $insertCols[] = "`{$col}`"; $insertMarks[] = ':' . $col; $insertParams[$col] = $value;
            };
            $addManualField('full_name', $fullName);
            $addManualField('phone', $phone !== '' ? $phone : null);
            $addManualField('email', $email !== '' ? $email : null);
            $addManualField('password_hash', password_hash($temporaryPassword, PASSWORD_DEFAULT));
            if (isset($studentColumns['username'])) $addManualField('username', $phone !== '' ? $phone : $email);
            if ($hasGrade) $addManualField('grade', $grade);
            if ($hasClassName) $addManualField('class_name', $className !== '' ? $className : null);
            foreach (['birth_date','date_of_birth','dob'] as $c) if (isset($studentColumns[$c]) && $birthDate !== '') { $addManualField($c, $birthDate); break; }
            foreach (['gender','sex'] as $c) if (isset($studentColumns[$c]) && $gender !== '') { $addManualField($c, $gender); break; }
            if (isset($studentColumns['status'])) $addManualField('status', 'active');
            if ($hasAcademicYearId && $postAcademicYearId > 0) $addManualField('academic_year_id', $postAcademicYearId);

            if ($currentUserRole === 'teacher') {
                if ($teacherScopeProvinceId > 0) $addManualField('province_id', $teacherScopeProvinceId);
                if ($teacherScopeSchoolId > 0) $addManualField('school_id', $teacherScopeSchoolId);
                if ($teacherScopeProvince !== '') $addManualField('province', $teacherScopeProvince);
                if ($teacherScopeSchool !== '') $addManualField('school_name', $teacherScopeSchool);
            } elseif ($currentUserRole === 'admin') {
                $addManualField('province_id', $adminProvinceId);
                $addManualField('school_id', $adminSchoolId);
                $addManualField('province', $adminProvinceLabel);
                $addManualField('school_name', $adminSchoolLabel);
            }

            $stmt = $pdo->prepare('INSERT INTO students (' . implode(', ', $insertCols) . ') VALUES (' . implode(', ', $insertMarks) . ')');
            $stmt->execute($insertParams);
            $tempPasswordResult = ['name'=>$fullName, 'phone'=>$phone, 'email'=>$email, 'password'=>$temporaryPassword];
            $flashSuccess = 'Đã thêm học sinh thủ công. Mật khẩu ban đầu: 12345678.';
            $_SESSION['teacher_csrf_token'] = bin2hex(random_bytes(32));
        } catch (Throwable $e) {
            error_log('Teacher manual create student error: ' . $e->getMessage());
            $flashError = $e instanceof RuntimeException ? $e->getMessage() : 'Không thể thêm học sinh.';
        }
    } 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 = '12345678'; $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);

                // Học sinh do giáo viên import phải thuộc đúng Tỉnh/Trường của giáo viên.
                // ID là nguồn chuẩn; text chỉ lưu kèm để tương thích dữ liệu cũ.
                if ($currentUserRole === 'teacher' && $teacherScopeReady) {
                    if ($teacherScopeProvinceId > 0) $addField('province_id', $teacherScopeProvinceId);
                    if ($teacherScopeSchoolId > 0) $addField('school_id', $teacherScopeSchoolId);
                    if ($teacherScopeProvince !== '') $addField('province', $teacherScopeProvince);
                    if ($teacherScopeSchool !== '') $addField('school_name', $teacherScopeSchool);
                }

                $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.');
            }

            if ($currentUserRole !== 'admin') {
                if (!$teacherScopeReady) {
                    throw new RuntimeException('Tài khoản giáo viên chưa có phạm vi trường hợp lệ.');
                }
                $checkPlaceholders = implode(',', array_fill(0, count($studentIds), '?'));
                $checkSql = "SELECT COUNT(*)
                             FROM students
                             WHERE id IN ($checkPlaceholders)
                               AND TRIM(province) = ?
                               AND TRIM(school_name) = ?";
                $checkStmt = $pdo->prepare($checkSql);
                $checkStmt->execute(array_merge($studentIds, [$teacherScopeProvince, $teacherScopeSchool]));
                if ((int)$checkStmt->fetchColumn() !== count($studentIds)) {
                    throw new RuntimeException('Có học sinh không thuộc trường của giáo viên. Thao tác đã bị chặn.');
                }
            }

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

            if ($hasGrade) {
                if ($hasAcademicYearId && $selectedAcademicYearId > 0) {
                    $sql = "UPDATE students
                            SET grade = ?,
                                class_name = ?,
                                academic_year_id = ?" . esTouchSql($hasUpdatedAt) . "
                            WHERE id IN ($placeholders)";
                    $params = array_merge(
                        [$targetGrade, $targetClass, $selectedAcademicYearId],
                        $studentIds
                    );
                } else {
                    $sql = "UPDATE students
                            SET grade = ?,
                                class_name = ?" . esTouchSql($hasUpdatedAt) . "
                            WHERE id IN ($placeholders)";
                    $params = array_merge([$targetGrade, $targetClass], $studentIds);
                }
            } else {
                if ($hasAcademicYearId && $selectedAcademicYearId > 0) {
                    $sql = "UPDATE students
                            SET class_name = ?,
                                academic_year_id = ?" . esTouchSql($hasUpdatedAt) . "
                            WHERE id IN ($placeholders)";
                    $params = array_merge(
                        [$targetClass, $selectedAcademicYearId],
                        $studentIds
                    );
                } else {
                    $sql = "UPDATE students
                            SET class_name = ?" . esTouchSql($hasUpdatedAt) . "
                            WHERE id IN ($placeholders)";
                    $params = array_merge([$targetClass], $studentIds);
                }
            }

            $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}";
            if ($hasAcademicYearId && $selectedAcademicYearId > 0) {
                $flashSuccess .= " và đồng bộ năm học.";
            } else {
                $flashSuccess .= ".";
            }
            $_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 === 'delete_student') {
        $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['status'])) {
                throw new RuntimeException('Bảng students chưa có cột status.');
            }

            // Xóa thật học sinh để giải phóng SĐT/Gmail cho lần import sau.
            // Các bảng tiến độ/XP có thể có khóa ngoại tới students, vì vậy chỉ xóa
            // khi cấu trúc CSDL cho phép; nếu bị ràng buộc, báo lỗi thay vì xóa dở dữ liệu.
            $stmt = $pdo->prepare(
                "DELETE FROM students
                 WHERE id = :id" .
                 esTeacherStudentScopeSql($currentUserRole, $teacherScopeReady)
            );
            $params = ['id' => $studentId];
            $params += esTeacherStudentScopeParams(
                $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 = 'Đã xóa học sinh. SĐT/Gmail đã được giải phóng và có thể dùng để import lại.';
            $_SESSION['teacher_csrf_token'] = bin2hex(random_bytes(32));
        } catch (Throwable $e) {
            error_log('Teacher delete student error: ' . $e->getMessage());
            $flashError = $e instanceof RuntimeException ? $e->getMessage() : 'Không thể xóa 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" .
                 esTeacherStudentScopeSql($currentUserRole, $teacherScopeReady)
            );
            $params = ['status' => $newStatus, 'id' => $studentId];
            $params += esTeacherStudentScopeParams(
                $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 === 'archive_class') {
        $sourceClass = trim((string)($_POST['source_class'] ?? ''));
        $postAcademicYearId = max(0, (int)($_POST['academic_year_id'] ?? 0));

        try {
            if (!$hasClassName) {
                throw new RuntimeException('Bảng students chưa có cột class_name.');
            }
            if ($sourceClass === '') {
                throw new RuntimeException('Hãy chọn lớp cần lưu trữ.');
            }

            $stmt = $pdo->prepare(
                "UPDATE students
                 SET status = 'suspended'" . esTouchSql($hasUpdatedAt) . "
                 WHERE class_name = :class_name
                   AND status = 'active'" . ($hasAcademicYearId && $postAcademicYearId > 0 ? " AND academic_year_id = :academic_year_id" : "") . esTeacherStudentScopeSql($currentUserRole, $teacherScopeReady)
            );
            $params = ['class_name' => $sourceClass];
            if ($hasAcademicYearId && $postAcademicYearId > 0) $params['academic_year_id'] = $postAcademicYearId;
            $params += esTeacherStudentScopeParams(
                $currentUserRole,
                $teacherScopeReady,
                $teacherScopeProvince,
                $teacherScopeSchool
            );
            $stmt->execute($params);
            $count = $stmt->rowCount();
            $flashSuccess = "Đã lưu trữ lớp {$sourceClass}: {$count} học sinh. Toàn bộ XP và tiến độ vẫn được giữ.";
            $_SESSION['teacher_csrf_token'] = bin2hex(random_bytes(32));
        } catch (Throwable $e) {
            error_log('Teacher archive class error: ' . $e->getMessage());
            $flashError = $e instanceof RuntimeException ? $e->getMessage() : 'Không thể lưu trữ lớp.';
        }
    } elseif ($action === 'restore_class') {
        $sourceClass = trim((string)($_POST['source_class'] ?? ''));
        $postAcademicYearId = max(0, (int)($_POST['academic_year_id'] ?? 0));

        try {
            if (!$hasClassName) {
                throw new RuntimeException('Bảng students chưa có cột class_name.');
            }
            if ($sourceClass === '') {
                throw new RuntimeException('Hãy chọn lớp cần khôi phục.');
            }

            $stmt = $pdo->prepare(
                "UPDATE students
                 SET status = 'active'" . esTouchSql($hasUpdatedAt) . "
                 WHERE class_name = :class_name
                   AND status = 'suspended'" . ($hasAcademicYearId && $postAcademicYearId > 0 ? " AND academic_year_id = :academic_year_id" : "") . esTeacherStudentScopeSql($currentUserRole, $teacherScopeReady)
            );
            $params = ['class_name' => $sourceClass];
            if ($hasAcademicYearId && $postAcademicYearId > 0) $params['academic_year_id'] = $postAcademicYearId;
            $params += esTeacherStudentScopeParams(
                $currentUserRole,
                $teacherScopeReady,
                $teacherScopeProvince,
                $teacherScopeSchool
            );
            $stmt->execute($params);
            $count = $stmt->rowCount();
            $flashSuccess = "Đã khôi phục lớp {$sourceClass}: {$count} học sinh. XP, tiến độ và lịch sử học vẫn được giữ nguyên.";
            $_SESSION['teacher_csrf_token'] = bin2hex(random_bytes(32));
        } catch (Throwable $e) {
            error_log('Teacher restore class error: ' . $e->getMessage());
            $flashError = $e instanceof RuntimeException ? $e->getMessage() : 'Không thể khôi phục lớp.';
        }
    } 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));

        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.');
            }

            $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 .= esTeacherStudentScopeSql($currentUserRole, $teacherScopeReady);
                $params += esTeacherStudentScopeParams(
                    $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), '?'));

                $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));
        $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.');
            }

            $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" : "") . esTeacherStudentScopeSql($currentUserRole, $teacherScopeReady)
            );
            $countParams = ['class_name' => $sourceClass];
            if ($hasAcademicYearId && $postAcademicYearId > 0) $countParams['academic_year_id'] = $postAcademicYearId;
            $countParams += esTeacherStudentScopeParams(
                $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.');
            }

            $update = $pdo->prepare(
                "UPDATE students
                 SET grade = :grade,
                     class_name = :target_class" . esTouchSql($hasUpdatedAt) . "
                 WHERE class_name = :source_class
                   AND status = 'active'" . ($hasAcademicYearId && $postAcademicYearId > 0 ? " AND academic_year_id = :academic_year_id" : "") . esTeacherStudentScopeSql($currentUserRole, $teacherScopeReady)
            );
            $updateParams = [
                'grade' => $targetGrade,
                'target_class' => $targetClass,
                'source_class' => $sourceClass,
            ];
            if ($hasAcademicYearId && $postAcademicYearId > 0) $updateParams['academic_year_id'] = $postAcademicYearId;
            $updateParams += esTeacherStudentScopeParams(
                $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_direct') {
        $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.');
            }

            // Teacher chỉ được reset học sinh thuộc đúng phạm vi trường của mình.
            $studentStmt = $pdo->prepare(
                "SELECT id, full_name, phone, email
                 FROM students
                 WHERE id = :student_id
                   AND status <> 'deleted'" .
                 esTeacherStudentScopeSql($currentUserRole, $teacherScopeReady) . "
                 LIMIT 1"
            );
            $params = ['student_id' => $studentId];
            $params += esTeacherStudentScopeParams(
                $currentUserRole,
                $teacherScopeReady,
                $teacherScopeProvince,
                $teacherScopeSchool
            );
            $studentStmt->execute($params);
            $studentRow = $studentStmt->fetch(PDO::FETCH_ASSOC);

            if (!$studentRow) {
                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.');
            }

            $temporaryPassword = '12345678';
            $passwordHash = password_hash($temporaryPassword, PASSWORD_DEFAULT);

            $update = $pdo->prepare(
                "UPDATE students
                 SET password_hash = :password_hash" . esTouchSql($hasUpdatedAt) . "
                 WHERE id = :student_id"
            );
            $update->execute([
                'password_hash' => $passwordHash,
                'student_id' => $studentId,
            ]);

            // Nếu học sinh từng gửi yêu cầu quên mật khẩu đang pending, đánh dấu đã xử lý.
            if (esTableExists($pdo, 'password_reset_requests')) {
                $requestCols = esTableColumns($pdo, 'password_reset_requests');
                $setParts = ["status = 'completed'"];
                if (isset($requestCols['completed_at'])) $setParts[] = "completed_at = NOW()";
                if (isset($requestCols['updated_at'])) $setParts[] = "updated_at = NOW()";

                $completeRequests = $pdo->prepare(
                    "UPDATE password_reset_requests
                     SET " . implode(', ', $setParts) . "
                     WHERE student_id = :student_id
                       AND status = 'pending'"
                );
                $completeRequests->execute(['student_id' => $studentId]);
            }

            $tempPasswordResult = [
                'name' => (string)$studentRow['full_name'],
                'phone' => (string)($studentRow['phone'] ?? ''),
                'email' => (string)($studentRow['email'] ?? ''),
                'password' => $temporaryPassword,
            ];
            $flashSuccess = 'Đã đặt lại mật khẩu học sinh. Mật khẩu tạm chỉ hiển thị một lần bên dưới.';
            $_SESSION['teacher_csrf_token'] = bin2hex(random_bytes(32));
        } catch (Throwable $e) {
            error_log('Teacher direct reset student password error: ' . $e->getMessage());
            $flashError = $e instanceof RuntimeException
                ? $e->getMessage()
                : 'Không thể đặt lại mật khẩu 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" .
                 esTeacherStudentScopeSql($currentUserRole, $teacherScopeReady, 's') . "
                 FOR UPDATE"
            );
            $params = ['request_id' => $requestId];
            $params += esTeacherStudentScopeParams(
                $currentUserRole,
                $teacherScopeReady,
                $teacherScopeProvince,
                $teacherScopeSchool
            );
            $stmt->execute($params);
            $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 = '12345678';
            $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.';
        }
    }
}

$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'" .
     esTeacherStudentScopeSql($currentUserRole, $teacherScopeReady, 's') . "
     ORDER BY r.requested_at ASC";
$pendingStmt = $pdo->prepare($pendingSql);
$pendingStmt->execute(esTeacherStudentScopeParams(
    $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';
if ($hasAcademicYearId) $studentSelect[] = 'academic_year_id';
if ($provinceField !== null) $studentSelect[] = $provinceField;
if ($schoolField !== null) $studentSelect[] = $schoolField;

$studentYearSql = '';
$studentListParams = esTeacherStudentScopeParams(
    $currentUserRole,
    $teacherScopeReady,
    $teacherScopeProvince,
    $teacherScopeSchool
);
if ($hasAcademicYearId && $currentAcademicYearId > 0) {
    $studentYearSql = " AND academic_year_id = :student_current_year";
    $studentListParams['student_current_year'] = $currentAcademicYearId;
}

$studentsSql =
    "SELECT " . implode(', ', $studentSelect) . "
     FROM students
     WHERE status <> 'deleted'" .
     $studentYearSql .
     esTeacherStudentScopeSql($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($studentListParams);
$students = $studentsStmt->fetchAll(PDO::FETCH_ASSOC);

/**
 * Build readable labels for province/school IDs when lookup tables exist.
 * Falls back to the raw value stored in students.
 */
$provinceLabelMap = [];
$schoolLabelMap = [];
$schoolProvinceMap = [];

if ($provinceField === 'province_id' && esTableExists($pdo, 'provinces')) {
    $cols = esTableColumns($pdo, 'provinces');
    $nameCol = null;
    foreach (['name', 'province_name', 'title'] as $candidate) {
        if (isset($cols[$candidate])) { $nameCol = $candidate; break; }
    }
    if ($nameCol !== null && isset($cols['id'])) {
        foreach ($pdo->query("SELECT id, `" . $nameCol . "` AS label FROM provinces ORDER BY `" . $nameCol . "`")->fetchAll(PDO::FETCH_ASSOC) as $row) {
            $provinceLabelMap[(string)$row['id']] = (string)$row['label'];
        }
    }
}

if ($schoolField === 'school_id' && esTableExists($pdo, 'schools')) {
    $cols = esTableColumns($pdo, 'schools');
    $nameCol = null;
    foreach (['name', 'school_name', 'title'] as $candidate) {
        if (isset($cols[$candidate])) { $nameCol = $candidate; break; }
    }
    if ($nameCol !== null && isset($cols['id'])) {
        $select = "SELECT id, `" . $nameCol . "` AS label";
        if (isset($cols['province_id'])) $select .= ", province_id";
        $select .= " FROM schools ORDER BY `" . $nameCol . "`";
        foreach ($pdo->query($select)->fetchAll(PDO::FETCH_ASSOC) as $row) {
            $schoolLabelMap[(string)$row['id']] = (string)$row['label'];
            if (isset($row['province_id'])) {
                $schoolProvinceMap[(string)$row['id']] = (string)$row['province_id'];
            }
        }
    }
}

$provinceOptions = [];
$schoolOptions = [];

// Admin cần toàn bộ Tỉnh/Trường để có thể thêm học sinh mới vào trường chưa có học sinh.
if ($currentUserRole === 'admin') {
    foreach ($provinceLabelMap as $value => $label) {
        $provinceOptions[$value] = $label;
    }
    foreach ($schoolLabelMap as $value => $label) {
        $schoolOptions[$value] = [
            'label' => $label,
            'province' => $schoolProvinceMap[$value] ?? '',
        ];
    }
}

foreach ($students as $studentRow) {
    $provinceValue = $provinceField !== null ? trim((string)($studentRow[$provinceField] ?? '')) : '';
    $schoolValue = $schoolField !== null ? trim((string)($studentRow[$schoolField] ?? '')) : '';

    if ($provinceValue !== '') {
        $provinceOptions[$provinceValue] = $provinceLabelMap[$provinceValue] ?? $provinceValue;
    }

    if ($schoolValue !== '') {
        $schoolProvince = $provinceValue;
        if ($schoolField === 'school_id' && isset($schoolProvinceMap[$schoolValue])) {
            $schoolProvince = $schoolProvinceMap[$schoolValue];
        }
        $schoolOptions[$schoolValue] = [
            'label' => $schoolLabelMap[$schoolValue] ?? $schoolValue,
            'province' => $schoolProvince,
        ];
    }
}

uasort($provinceOptions, static fn($a, $b) => strnatcasecmp((string)$a, (string)$b));
uasort($schoolOptions, static fn($a, $b) => strnatcasecmp((string)$a['label'], (string)$b['label']));

$classOptions = [];
$archivedClassOptions = [];
if ($hasClassName) {
    $yearWhere = '';
    $yearParams = [];
    if ($hasAcademicYearId && $selectedAcademicYearId > 0) {
        $yearWhere = " AND academic_year_id = :academic_year_id";
        $yearParams['academic_year_id'] = $selectedAcademicYearId;
    }
    $yearWhere .= esTeacherStudentScopeSql($currentUserRole, $teacherScopeReady);
    $yearParams += esTeacherStudentScopeParams(
        $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);
}

$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}
.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,.btn-delete{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}
.btn-delete{background:#fff1f2;color:#be123c;border:1px solid #fecdd3}
.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}
.student-list-tools{display:flex;gap:9px;align-items:center;flex-wrap:wrap}.manual-add-btn{border:0;border-radius:10px;padding:10px 14px;background:#2563eb;color:#fff;font-weight:800;cursor:pointer}.manual-add-btn:hover{background:#1d4ed8}.manual-dialog{border:0;border-radius:18px;padding:0;width:min(680px,calc(100vw - 28px));box-shadow:0 24px 70px rgba(15,23,42,.25)}.manual-dialog::backdrop{background:rgba(15,23,42,.45)}.manual-form{padding:20px}.manual-form-head{display:flex;justify-content:space-between;gap:12px;align-items:flex-start;margin-bottom:16px}.manual-form-head h3{margin:0;color:#15233c}.manual-close{border:0;background:#f1f5f9;border-radius:9px;width:36px;height:36px;cursor:pointer;font-size:18px}.manual-grid{display:grid;grid-template-columns:1fr 1fr;gap:12px}.manual-grid label{display:grid;gap:6px;font-size:12px;font-weight:750;color:#617085}.manual-grid input,.manual-grid select{width:100%;min-height:42px;border:1px solid #dbe3ee;border-radius:10px;padding:8px 10px;font:inherit}.manual-full{grid-column:1/-1}.manual-actions{display:flex;justify-content:flex-end;gap:9px;margin-top:18px}.manual-cancel{border:1px solid #dbe3ee;background:#fff;border-radius:10px;padding:10px 14px;font-weight:750;cursor:pointer}.pagination{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;padding:14px 20px;border-top:1px solid #edf1f6}.pagination-info{font-size:12.5px;color:#64748b;font-weight:700}.pagination-buttons{display:flex;gap:6px;flex-wrap:wrap}.page-btn{min-width:36px;height:36px;border:1px solid #dbe3ee;background:#fff;border-radius:9px;cursor:pointer;font-weight:750;color:#334155}.page-btn.active{background:#2563eb;color:#fff;border-color:#2563eb}.page-btn:disabled{opacity:.45;cursor:not-allowed}@media(max-width:650px){.manual-grid{grid-template-columns:1fr}.manual-full{grid-column:auto}}
@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}}


.reset-disclosure{background:#fff;border:1px solid #e6ebf2;border-radius:18px;box-shadow:0 5px 18px rgba(18,38,63,.05);overflow:hidden}
.reset-disclosure summary{list-style:none;cursor:pointer;padding:16px 20px;display:flex;align-items:center;justify-content:space-between;gap:14px}
.reset-disclosure summary::-webkit-details-marker{display:none}
.reset-disclosure summary:hover{background:#fbfdff}
.reset-summary-title{display:flex;align-items:center;gap:8px;font-size:17px;font-weight:800;color:#15233c}
.reset-summary-meta{display:flex;align-items:center;gap:10px;color:#718096;font-size:12.5px}
.reset-count-badge{display:inline-flex;align-items:center;justify-content:center;min-width:24px;height:24px;padding:0 8px;border-radius:999px;background:#fff4df;color:#a65b00;font-weight:800}
.reset-arrow{font-size:13px;color:#64748b;transition:transform .2s ease}
.reset-disclosure[open] .reset-arrow{transform:rotate(180deg)}
.reset-disclosure .reset-content{border-top:1px solid #edf1f6}
.reset-disclosure .empty-state{padding:18px 20px}

</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="active" href="index.php">👥 Học sinh</a>
        <a class="" 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; ?>


    <details class="reset-disclosure" <?= count($pendingRequests) > 0 ? 'open' : '' ?>>
        <summary>
            <div class="reset-summary-title">🔑 Yêu cầu đặt lại mật khẩu</div>
            <div class="reset-summary-meta">
                <span class="reset-count-badge"><?= count($pendingRequests) ?></span>
                <span><?= count($pendingRequests) > 0 ? 'yêu cầu chờ xử lý' : 'Không có yêu cầu' ?></span>
                <span class="reset-arrow">▼</span>
            </div>
        </summary>
        <div class="reset-content">
        <?php if ($pendingRequests === []): ?>
            <div class="empty-state">Hiện không có yêu cầu nào đang chờ.</div>
        <?php else: ?>
            <div class="table-wrap">
                <table class="student-table">
                    <thead>
                    <tr>
                        <th>Học sinh</th>
                        <th>Số điện thoại</th>
                        <th>Gmail</th>
                        <th>Gửi lúc</th>
                        <th>Trạng thái</th>
                        <th></th>
                    </tr>
                    </thead>
                    <tbody>
                    <?php foreach ($pendingRequests as $request): ?>
                        <tr>
                            <td>
                                <div class="student-name"><?= htmlspecialchars((string)$request['full_name'], ENT_QUOTES, 'UTF-8') ?></div>
                                <div class="muted">ID <?= (int)$request['student_id'] ?></div>
                            </td>
                            <td><?= htmlspecialchars((string)($request['phone'] ?? ''), ENT_QUOTES, 'UTF-8') ?></td>
                            <td><?= htmlspecialchars((string)($request['email'] ?? ''), ENT_QUOTES, 'UTF-8') ?></td>
                            <td><?= htmlspecialchars((string)$request['requested_at'], ENT_QUOTES, 'UTF-8') ?></td>
                            <td><span class="badge badge-pending">Chờ reset</span></td>
                            <td>
                                <form method="post" onsubmit="return confirm('Đặt lại mật khẩu cho 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="reset_password">
                                    <input type="hidden" name="request_id" value="<?= (int)$request['request_id'] ?>">
                                    <button type="submit" class="reset-btn">🔑 Đặt lại mật khẩu</button>
                                </form>
                            </td>
                        </tr>
                    <?php endforeach; ?>
                    </tbody>
                </table>
            </div>
        <?php endif; ?>

        </div>
    </details>

    <section class="panel">
        <div class="panel-head">
            <div>
                <h2>Danh sách học sinh<?= $currentAcademicYearLabel !== '' ? ' — Năm học ' . htmlspecialchars($currentAcademicYearLabel, ENT_QUOTES, 'UTF-8') : '' ?></h2>
                <p>
                    <?= count($students) ?> học sinh<?= $currentAcademicYearLabel !== '' ? ' trong năm học ' . htmlspecialchars($currentAcademicYearLabel, ENT_QUOTES, 'UTF-8') : '' ?>.
                    <a href="academic-years.php" style="margin-left:8px;color:#2563eb;font-weight:750;text-decoration:none;">Xem năm học trước →</a>
                </p>
            </div>
            <div class="student-list-tools">
                <button type="button" class="manual-add-btn" onclick="document.getElementById('manualStudentDialog').showModal()">＋ Thêm học sinh</button>
                <a class="reset-btn" href="import.php" style="text-decoration:none;display:inline-flex;align-items:center;">📥 Import Excel</a>
            </div>
        </div>

        <?php if ($currentUserRole === 'teacher' && $teacherScopeReady): ?>
        <div style="margin:14px 20px 0;padding:10px 12px;border:1px solid #dbeafe;border-radius:10px;background:#eff6ff;color:#1e40af;font-size:12.5px;">
            <?php
                // Luôn hiển thị tên hiện tại từ bảng chuẩn theo ID.
                // Nếu Admin đổi tên Tỉnh/Trường, trang này đổi theo ngay.
                $scopeProvinceLabel = $teacherScopeProvinceId > 0
                    ? ($provinceLabelMap[(string)$teacherScopeProvinceId] ?? $teacherScopeProvince)
                    : $teacherScopeProvince;
                $scopeSchoolLabel = $teacherScopeSchoolId > 0
                    ? ($schoolLabelMap[(string)$teacherScopeSchoolId] ?? $teacherScopeSchool)
                    : $teacherScopeSchool;
            ?>
            🏫 <strong><?= htmlspecialchars($scopeSchoolLabel, ENT_QUOTES, 'UTF-8') ?></strong>
            <span style="color:#64748b;">• <?= htmlspecialchars($scopeProvinceLabel, ENT_QUOTES, 'UTF-8') ?></span>
            <?php if ($currentAcademicYearLabel !== ''): ?>
                <span style="color:#64748b;">• 📅 Năm học hiện hành: <strong style="color:#1e40af;"><?= htmlspecialchars($currentAcademicYearLabel, ENT_QUOTES, 'UTF-8') ?></strong></span>
            <?php endif; ?>
        </div>
        <?php endif; ?>

        <div class="filter-bar">
            <div class="filter-caption">🔎 Tìm kiếm & lọc</div>
            <label>
                Tìm học sinh
                <input id="studentSearch" type="search" placeholder="Tên, SĐT, Gmail...">
            </label>
            <?php if ($currentUserRole === 'admin' && $provinceField !== null): ?>
            <label>
                Tỉnh
                <select id="studentProvinceFilter">
                    <option value="">Tất cả tỉnh</option>
                    <?php foreach ($provinceOptions as $value => $label): ?>
                        <option value="<?= htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8') ?>"><?= htmlspecialchars((string)$label, ENT_QUOTES, 'UTF-8') ?></option>
                    <?php endforeach; ?>
                </select>
            </label>
            <?php endif; ?>

            <?php if ($currentUserRole === 'admin' && $schoolField !== null): ?>
            <label>
                Trường
                <select id="studentSchoolFilter">
                    <option value="">Tất cả trường</option>
                    <?php foreach ($schoolOptions as $value => $info): ?>
                        <option
                            value="<?= htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8') ?>"
                            data-province="<?= htmlspecialchars((string)($info['province'] ?? ''), ENT_QUOTES, 'UTF-8') ?>"
                        ><?= htmlspecialchars((string)$info['label'], ENT_QUOTES, 'UTF-8') ?></option>
                    <?php endforeach; ?>
                </select>
            </label>
            <?php endif; ?>

            <?php if ($hasGrade): ?>
            <label>
                Khối
                <select id="studentGradeFilter">
                    <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>
            <?php endif; ?>
            <?php if ($hasClassName): ?>
            <label>
                Lớp
                <select id="studentClassFilter">
                    <option value="">Tất cả lớp</option>
                    <option value="__none__">Chưa có lớp</option>
                    <?php foreach ($studentFilterClasses as $className): ?>
                        <option value="<?= htmlspecialchars((string)$className, ENT_QUOTES, 'UTF-8') ?>"><?= htmlspecialchars((string)$className, ENT_QUOTES, 'UTF-8') ?></option>
                    <?php endforeach; ?>
                </select>
            </label>
            <?php endif; ?>
            <label>
                Trạng thái
                <select id="studentStatusFilter">
                    <option value="">Tất cả trạng thái</option>
                    <option value="active">Active</option>
                    <option value="suspended">Lưu trữ</option>
                </select>
            </label>
        </div>

        <?php if ($hasClassName): ?>
        <form method="post" id="assignClassForm" onsubmit="return confirmAssignClass();">
            <input type="hidden" name="csrf_token" value="<?= htmlspecialchars((string)$_SESSION['teacher_csrf_token'], ENT_QUOTES, 'UTF-8') ?>">
            <input type="hidden" name="action" value="assign_students_class">

            <div class="assign-bar">
                <div class="assign-caption">👥 Gán / chuyển lớp cho học sinh đã chọn</div>
                <?php if ($hasGrade): ?>
                <label>
                    Khối mới
                    <select name="assign_grade" required>
                        <option value="">Chọn khối</option>
                        <?php for ($g = 1; $g <= 5; $g++): ?>
                            <option value="<?= $g ?>">Khối <?= $g ?></option>
                        <?php endfor; ?>
                    </select>
                </label>
                <?php endif; ?>

                <label>
                    Lớp mới
                    <input type="text" name="assign_class" list="assignClassOptions" placeholder="Chọn hoặc nhập lớp mới" autocomplete="off" required>
                    <datalist id="assignClassOptions">
                        <?php foreach ($studentFilterClasses as $className): ?>
                            <option value="<?= htmlspecialchars((string)$className, ENT_QUOTES, 'UTF-8') ?>"></option>
                        <?php endforeach; ?>
                    </datalist>
                </label>

                <button type="submit" class="assign-btn">👥 Gán / chuyển lớp</button>
                <span class="assign-count" id="assignSelectedCount">0 học sinh được chọn</span>
            </div>
        </form>
        <?php endif; ?>

        <dialog class="manual-dialog" id="manualStudentDialog">
            <form method="post" class="manual-form">
                <input type="hidden" name="csrf_token" value="<?= htmlspecialchars((string)$_SESSION['teacher_csrf_token'], ENT_QUOTES, 'UTF-8') ?>">
                <input type="hidden" name="action" value="create_student_manual">
                <input type="hidden" name="academic_year_id" value="<?= (int)$selectedAcademicYearId ?>">
                <div class="manual-form-head">
                    <div><h3>＋ Thêm học sinh</h3><div class="muted" style="margin-top:5px"><?= $currentUserRole === 'admin' ? 'Chọn Tỉnh và Trường trước khi thêm học sinh.' : 'Thêm nhanh một học sinh vào trường hiện tại.' ?></div></div>
                    <button type="button" class="manual-close" onclick="document.getElementById('manualStudentDialog').close()">×</button>
                </div>
                <div class="manual-grid">
                    <label class="manual-full">Họ và tên *<input type="text" name="full_name" required autocomplete="off"></label>
                    <label>Số điện thoại<input type="text" name="phone" autocomplete="off"></label>
                    <label>Gmail<input type="email" name="email" autocomplete="off"></label>
                    <?php if ($currentUserRole === 'admin'): ?>
                        <label>Tỉnh *
                            <select name="admin_province_id" id="manualAdminProvince" required>
                                <option value="">Chọn tỉnh</option>
                                <?php foreach ($provinceOptions as $value => $label): ?>
                                    <option value="<?= htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8') ?>"><?= htmlspecialchars((string)$label, ENT_QUOTES, 'UTF-8') ?></option>
                                <?php endforeach; ?>
                            </select>
                        </label>
                        <label>Trường *
                            <select name="admin_school_id" id="manualAdminSchool" required disabled>
                                <option value="">Chọn trường</option>
                                <?php foreach ($schoolOptions as $value => $info): ?>
                                    <option value="<?= htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8') ?>" data-province="<?= htmlspecialchars((string)($info['province'] ?? ''), ENT_QUOTES, 'UTF-8') ?>"><?= htmlspecialchars((string)($info['label'] ?? $value), ENT_QUOTES, 'UTF-8') ?></option>
                                <?php endforeach; ?>
                            </select>
                        </label>
                    <?php endif; ?>
                    <?php if ($hasGrade): ?><label>Khối *<select name="grade" required><option value="">Chọn khối</option><?php for ($g=1;$g<=5;$g++): ?><option value="<?= $g ?>">Khối <?= $g ?></option><?php endfor; ?></select></label><?php endif; ?>
                    <?php if ($hasClassName): ?><label>Lớp<input type="text" name="class_name" list="manualClassOptions" placeholder="Ví dụ: 4/1" autocomplete="off"><datalist id="manualClassOptions"><?php foreach ($studentFilterClasses as $className): ?><option value="<?= htmlspecialchars((string)$className, ENT_QUOTES, 'UTF-8') ?>"></option><?php endforeach; ?></datalist></label><?php endif; ?>
                    <label>Ngày sinh<input type="date" name="birth_date"></label>
                    <label>Giới tính<select name="gender"><option value="">Chọn</option><option value="male">Nam</option><option value="female">Nữ</option></select></label>
                </div>
                <div class="bulk-create-help">Cần ít nhất SĐT hoặc Gmail. Tài khoản đăng nhập dùng SĐT (ưu tiên) hoặc Gmail. Mật khẩu ban đầu: <strong>12345678</strong>.</div>
                <div class="manual-actions"><button type="button" class="manual-cancel" onclick="document.getElementById('manualStudentDialog').close()">Hủy</button><button type="submit" class="manual-add-btn">Thêm học sinh</button></div>
            </form>
        </dialog>

        <div class="table-wrap">
            <table class="student-table" id="studentsTable">
                <thead>
                <tr>
                    <?php if ($hasClassName): ?>
                        <th style="width:42px"><input type="checkbox" id="selectAllStudents" class="student-check" aria-label="Chọn tất cả học sinh"></th>
                    <?php endif; ?>
                    <th>Học sinh</th>
                    <?php if ($hasGrade): ?><th>Khối</th><?php endif; ?>
                    <?php if ($hasClassName): ?><th>Lớp</th><?php endif; ?>
                    <th>Số điện thoại</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 ($students as $student): ?>
                    <tr
                        data-province="<?= htmlspecialchars((string)($provinceField !== null ? ($student[$provinceField] ?? '') : ''), ENT_QUOTES, 'UTF-8') ?>"
                        data-school="<?= htmlspecialchars((string)($schoolField !== null ? ($student[$schoolField] ?? '') : ''), ENT_QUOTES, 'UTF-8') ?>"
                        data-grade="<?= htmlspecialchars((string)($student['grade'] ?? ''), ENT_QUOTES, 'UTF-8') ?>"
                        data-class="<?= htmlspecialchars((string)($student['class_name'] ?? ''), ENT_QUOTES, 'UTF-8') ?>"
                        data-status="<?= htmlspecialchars((string)($student['status'] ?? ''), ENT_QUOTES, 'UTF-8') ?>"
                    >
                        <?php if ($hasClassName): ?>
                            <td>
                                <input
                                    type="checkbox"
                                    class="student-check student-select"
                                    name="student_ids[]"
                                    value="<?= (int)$student['id'] ?>"
                                    form="assignClassForm"
                                    aria-label="Chọn <?= htmlspecialchars((string)$student['full_name'], ENT_QUOTES, 'UTF-8') ?>"
                                >
                            </td>
                        <?php endif; ?>
                        <td>
                            <div class="student-name"><?= htmlspecialchars((string)$student['full_name'], ENT_QUOTES, 'UTF-8') ?></div>
                            <div class="muted">ID <?= (int)$student['id'] ?></div>
                        </td>
                        <?php if ($hasGrade): ?><td><?= htmlspecialchars((string)($student['grade'] ?? ''), ENT_QUOTES, 'UTF-8') ?></td><?php endif; ?>
                        <?php if ($hasClassName): ?><td><?= htmlspecialchars((string)($student['class_name'] ?? ''), ENT_QUOTES, 'UTF-8') ?></td><?php endif; ?>
                        <td><?= htmlspecialchars((string)($student['phone'] ?? ''), ENT_QUOTES, 'UTF-8') ?></td>
                        <td><?= htmlspecialchars((string)($student['email'] ?? ''), ENT_QUOTES, 'UTF-8') ?></td>
                        <td>
                            <?php if (($student['status'] ?? '') === 'active'): ?>
                                <span class="badge badge-active">Active</span>
                            <?php else: ?>
                                <span class="badge badge-inactive"><?= (($student['status'] ?? '') === 'suspended') ? 'Lưu trữ' : htmlspecialchars((string)$student['status'], ENT_QUOTES, 'UTF-8') ?></span>
                            <?php endif; ?>
                        </td>
                        <td><?= htmlspecialchars((string)($student['created_at'] ?? ''), ENT_QUOTES, 'UTF-8') ?></td>
                        <td>
                            <div class="student-actions">
                                <?php if (($student['status'] ?? '') !== 'deleted'): ?>
                                    <form method="post" onsubmit="return confirm('Đặt lại mật khẩu cho <?= htmlspecialchars((string)$student['full_name'], ENT_QUOTES, 'UTF-8') ?>? Mật khẩu cũ sẽ không còn sử dụng được.');">
                                        <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_direct">
                                        <input type="hidden" name="student_id" value="<?= (int)$student['id'] ?>">
                                        <button type="submit" class="reset-btn">🔑 Reset MK</button>
                                    </form>
                                <?php endif; ?>
                                <?php if (($student['status'] ?? '') === 'active'): ?>
                                    <form method="post" onsubmit="return confirm('Lưu trữ học sinh này? XP, tiến độ và lịch sử học vẫn được giữ.');">
                                        <input type="hidden" name="csrf_token" value="<?= htmlspecialchars((string)$_SESSION['teacher_csrf_token'], ENT_QUOTES, 'UTF-8') ?>">
                                        <input type="hidden" name="action" value="archive_student">
                                        <input type="hidden" name="student_id" value="<?= (int)$student['id'] ?>">
                                        <button type="submit" class="btn-archive">Lưu trữ</button>
                                    </form>
                                <?php else: ?>
                                    <form method="post" onsubmit="return confirm('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="restore_student">
                                        <input type="hidden" name="student_id" value="<?= (int)$student['id'] ?>">
                                        <button type="submit" class="btn-restore">Kích hoạt</button>
                                    </form>
                                <?php endif; ?>
                                <?php if (($student['status'] ?? '') !== 'deleted'): ?>
                                    <form method="post" onsubmit="return confirm('Xóa học sinh này khỏi danh sách? XP, tiến độ và lịch sử học 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="delete_student">
                                        <input type="hidden" name="student_id" value="<?= (int)$student['id'] ?>">
                                        <button type="submit" class="btn-delete">🗑 Xóa</button>
                                    </form>
                                <?php endif; ?>
                            </div>
                        </td>
                    </tr>
                <?php endforeach; ?>
                </tbody>
            </table>
        </div>
        <div class="pagination" id="studentPagination">
            <div class="pagination-info" id="paginationInfo"></div>
            <div class="pagination-buttons" id="paginationButtons"></div>
        </div>
    </section>
</div>

<script>
(function () {
    const manualProvince = document.getElementById('manualAdminProvince');
    const manualSchool = document.getElementById('manualAdminSchool');
    if (manualProvince && manualSchool) {
        const schoolOptions = Array.from(manualSchool.querySelectorAll('option[data-province]'));
        const filterManualSchools = () => {
            const provinceId = manualProvince.value;
            manualSchool.value = '';
            schoolOptions.forEach(option => {
                option.hidden = provinceId === '' || option.dataset.province !== provinceId;
                option.disabled = option.hidden;
            });
            manualSchool.disabled = provinceId === '';
        };
        manualProvince.addEventListener('change', filterManualSchools);
        filterManualSchools();
    }

    const input = document.getElementById('studentSearch');
    const provinceFilter = document.getElementById('studentProvinceFilter');
    const schoolFilter = document.getElementById('studentSchoolFilter');
    const gradeFilter = document.getElementById('studentGradeFilter');
    const classFilter = document.getElementById('studentClassFilter');
    const statusFilter = document.getElementById('studentStatusFilter');
    const rows = Array.from(document.querySelectorAll('#studentsTable tbody tr'));
    const pageSize = 20;
    let currentPage = 1;
    let filteredRows = rows.slice();
    const paginationInfo = document.getElementById('paginationInfo');
    const paginationButtons = document.getElementById('paginationButtons');

    function renderStudentPage() {
        const total = filteredRows.length;
        const totalPages = Math.max(1, Math.ceil(total / pageSize));
        if (currentPage > totalPages) currentPage = totalPages;
        const start = (currentPage - 1) * pageSize;
        const end = Math.min(start + pageSize, total);
        rows.forEach(row => row.style.display = 'none');
        filteredRows.slice(start, end).forEach(row => row.style.display = '');
        if (paginationInfo) paginationInfo.textContent = total === 0 ? 'Không có học sinh phù hợp' : `Hiển thị ${start + 1}–${end} / ${total} học sinh`;
        if (paginationButtons) {
            paginationButtons.innerHTML = '';
            const addButton = (label, page, disabled, active) => {
                const btn = document.createElement('button'); btn.type='button'; btn.className='page-btn' + (active ? ' active' : ''); btn.textContent=label; btn.disabled=disabled;
                btn.addEventListener('click', () => { currentPage=page; renderStudentPage(); refreshCount(); }); paginationButtons.appendChild(btn);
            };
            addButton('‹', Math.max(1,currentPage-1), currentPage===1, false);
            const from=Math.max(1,currentPage-2), to=Math.min(totalPages,from+4);
            for(let p=from;p<=to;p++) addButton(String(p),p,false,p===currentPage);
            addButton('›', Math.min(totalPages,currentPage+1), currentPage===totalPages, false);
        }
    }

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

        filteredRows = rows.filter(function (row) {
            const rowText = row.textContent.toLowerCase();
            const rowProvince = (row.dataset.province || '').trim();
            const rowSchool = (row.dataset.school || '').trim();
            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 matchProvince = province === '' || rowProvince === province;
            const matchSchool = school === '' || rowSchool === school;
            const matchGrade = grade === '' || rowGrade === grade;
            const matchClass = className === '' || (className === '__none__' ? rowClass === '' : rowClass === className);
            const matchStatus = status === '' || rowStatus === status;
            return matchSearch && matchProvince && matchSchool && matchGrade && matchClass && matchStatus;
        });
        currentPage = 1;
        renderStudentPage();
        refreshCount();
    }

    function refreshSchoolOptions() {
        if (!schoolFilter) return;
        const province = provinceFilter ? provinceFilter.value : '';

        Array.from(schoolFilter.options).forEach(function (option, index) {
            if (index === 0) return;
            const optionProvince = (option.dataset.province || '').trim();
            option.hidden = province !== '' && optionProvince !== '' && optionProvince !== province;
        });

        const selected = schoolFilter.options[schoolFilter.selectedIndex];
        if (selected && selected.hidden) {
            schoolFilter.value = '';
        }
    }

    if (input) input.addEventListener('input', applyStudentFilters);
    if (provinceFilter) provinceFilter.addEventListener('change', function () {
        refreshSchoolOptions();
        applyStudentFilters();
    });
    if (schoolFilter) schoolFilter.addEventListener('change', applyStudentFilters);
    if (gradeFilter) gradeFilter.addEventListener('change', applyStudentFilters);
    if (classFilter) classFilter.addEventListener('change', applyStudentFilters);
    if (statusFilter) statusFilter.addEventListener('change', applyStudentFilters);

    refreshSchoolOptions();

    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();

    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>
