<?php
declare(strict_types=1);

/**
 * EnglishSpeak - Students Management
 * Shared for Admin and Teacher
 */

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

$pdo = getDbConnection();

$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 = [];

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;
}


/**
 * Chuẩn hóa và kiểm tra SĐT di động Việt Nam cho trang Import học sinh.
 * Chấp nhận các dạng có khoảng trắng, dấu chấm, gạch ngang, +84/84.
 * Trả về dạng chuẩn 0xxxxxxxxx; trả về '' nếu không hợp lệ.
 */
function esNormalizeVietnamMobile(string $phone): string
{
    $phone = trim($phone);
    if ($phone === '') return '';

    // Chỉ giữ chữ số và dấu + ở đầu để xử lý các cách nhập phổ biến.
    $phone = preg_replace('/[\s.()\-]+/', '', $phone) ?? $phone;

    if (str_starts_with($phone, '+84')) {
        $phone = '0' . substr($phone, 3);
    } elseif (str_starts_with($phone, '84') && strlen($phone) === 11) {
        $phone = '0' . substr($phone, 2);
    }

    // SĐT di động Việt Nam: 10 số, bắt đầu 03/05/07/08/09.
    if (!preg_match('/^0(?:3|5|7|8|9)\d{8}$/', $phone)) {
        return '';
    }

    return $phone;
}


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

// Phạm vi quản lý của tài khoản đang đăng nhập.
// Teacher: học sinh import phải tự động thuộc đúng Tỉnh/Trường của teacher.
// Admin: không ép phạm vi trường ở màn import này.
$currentUserRole = strtolower(trim((string)($_SESSION['user_role'] ?? 'teacher')));
$currentUserId = max(0, (int)($_SESSION['user_id'] ?? 0));

$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 !== '';
    }
}

// Referral của giáo viên đang đăng nhập.
// Khi giáo viên import học sinh, web tự gắn referral; Excel không cần có cột mã referral.
$teacherReferralCode = '';
$referralTablesReady = false;

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

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

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

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

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

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

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

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

// Danh sách Tỉnh/Trường cho Admin chọn khi import học sinh.
// Teacher vẫn tự động dùng Tỉnh/Trường trong teacher_profiles như trước.
$adminProvinceOptions = [];
$adminSchoolOptions = [];

if ($currentUserRole === 'admin') {
    if (esTableExists($pdo, 'provinces')) {
        $provinceCols = esTableColumns($pdo, 'provinces');
        $provinceNameCol = null;
        foreach (['name', 'province_name', 'title'] as $candidate) {
            if (isset($provinceCols[$candidate])) {
                $provinceNameCol = $candidate;
                break;
            }
        }
        if ($provinceNameCol !== null && isset($provinceCols['id'])) {
            $adminProvinceOptions = $pdo->query(
                "SELECT id, `{$provinceNameCol}` AS label FROM provinces ORDER BY `{$provinceNameCol}`"
            )->fetchAll(PDO::FETCH_ASSOC);
        }
    }

    if (esTableExists($pdo, 'schools')) {
        $schoolCols = esTableColumns($pdo, 'schools');
        $schoolNameCol = null;
        foreach (['name', 'school_name', 'title'] as $candidate) {
            if (isset($schoolCols[$candidate])) {
                $schoolNameCol = $candidate;
                break;
            }
        }
        if ($schoolNameCol !== null && isset($schoolCols['id'])) {
            $schoolSql = "SELECT id, `{$schoolNameCol}` AS label";
            if (isset($schoolCols['province_id'])) $schoolSql .= ", province_id";
            $schoolSql .= " FROM schools ORDER BY `{$schoolNameCol}`";
            $adminSchoolOptions = $pdo->query($schoolSql)->fetchAll(PDO::FETCH_ASSOC);
        }
    }
}

// Chỉ chuẩn bị referral cho tài khoản Teacher. Admin import không tự gắn referral.
if ($currentUserRole === 'teacher' && $currentUserId > 0) {
    $referralTablesReady = esTableExists($pdo, 'teacher_referral_codes')
        && esTableExists($pdo, 'student_referrals');

    if ($referralTablesReady) {
        $refStmt = $pdo->prepare(
            "SELECT referral_code
             FROM teacher_referral_codes
             WHERE teacher_id = :teacher_id
               AND status = 'active'
             ORDER BY id DESC
             LIMIT 1"
        );
        $refStmt->execute(['teacher_id' => $currentUserId]);
        $teacherReferralCode = trim((string)($refStmt->fetchColumn() ?: ''));
    }
}

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

function esTeacherStudentScopeSql(
    string $currentUserRole,
    bool $teacherScopeReady,
    array $studentColumns,
    int $teacherScopeProvinceId,
    int $teacherScopeSchoolId
): string {
    if ($currentUserRole !== 'teacher') return '';
    if (!$teacherScopeReady) return ' AND 1 = 0';

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

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

function esTeacherStudentScopeParams(
    string $currentUserRole,
    bool $teacherScopeReady,
    array $studentColumns,
    int $teacherScopeProvinceId,
    int $teacherScopeSchoolId,
    string $teacherScopeProvince,
    string $teacherScopeSchool
): array {
    if ($currentUserRole !== 'teacher' || !$teacherScopeReady) 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' => $teacherScopeProvince,
        'scope_school' => $teacherScopeSchool,
    ];
}

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 === 'import_students') {
        $postAcademicYearId = max(0, (int)($_POST['academic_year_id'] ?? 0));
        $postAdminProvinceId = max(0, (int)($_POST['admin_province_id'] ?? 0));
        $postAdminSchoolId = max(0, (int)($_POST['admin_school_id'] ?? 0));
        $adminProvinceName = '';
        $adminSchoolName = '';

        try {
            if ($currentUserRole === 'teacher' && !$teacherScopeReady) {
                throw new RuntimeException(
                    'Tài khoản giáo viên chưa được gắn đầy đủ Tỉnh/Trường. Vui lòng liên hệ Admin.'
                );
            }

            if ($currentUserRole === 'admin') {
                if ($postAdminProvinceId <= 0 || $postAdminSchoolId <= 0) {
                    throw new RuntimeException('Admin phải chọn Tỉnh và Trường trước khi import học sinh.');
                }

                $provinceFound = null;
                foreach ($adminProvinceOptions as $option) {
                    if ((int)($option['id'] ?? 0) === $postAdminProvinceId) {
                        $provinceFound = $option;
                        break;
                    }
                }
                if (!$provinceFound) {
                    throw new RuntimeException('Tỉnh đã chọn không hợp lệ.');
                }
                $adminProvinceName = trim((string)($provinceFound['label'] ?? ''));

                $schoolFound = null;
                foreach ($adminSchoolOptions as $option) {
                    if ((int)($option['id'] ?? 0) !== $postAdminSchoolId) continue;
                    if (isset($option['province_id']) && (int)$option['province_id'] !== $postAdminProvinceId) continue;
                    $schoolFound = $option;
                    break;
                }
                if (!$schoolFound) {
                    throw new RuntimeException('Trường đã chọn không thuộc Tỉnh đã chọn hoặc không hợp lệ.');
                }
                $adminSchoolName = trim((string)($schoolFound['label'] ?? ''));
            }

            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;
                $phoneRaw=$get('phone');
                $phone=$phoneRaw !== '' ? esNormalizeVietnamMobile($phoneRaw) : '';
                $email=$get('email');

                if ($phoneRaw !== '' && $phone === '') {
                    $bulkCreateResults[]=[
                        'name'=>$fullName,
                        'login'=>$phoneRaw,
                        'password'=>'',
                        'status'=>'Bỏ qua: SĐT không hợp lệ',
                        'ok'=>false
                    ];
                    continue;
                }

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

                // Chuẩn hóa ngày sinh về định dạng MySQL DATE: YYYY-MM-DD.
                // Giáo viên có thể nhập dd/mm/yyyy, dd-mm-yyyy, yyyy-mm-dd,
                // hoặc ngày Excel dạng số serial.
                $birthDateRaw = trim($get('birth_date'));
                $birthDate = '';

                if ($birthDateRaw !== '') {
                    if (preg_match('/^(\\d{1,2})[\\/\\-.](\\d{1,2})[\\/\\-.](\\d{4})$/', $birthDateRaw, $m)) {
                        $day = (int)$m[1];
                        $month = (int)$m[2];
                        $year = (int)$m[3];

                        if (!checkdate($month, $day, $year)) {
                            $bulkCreateResults[] = [
                                'name'=>$fullName,
                                'login'=>$phone!==''?$phone:$email,
                                'password'=>'',
                                'status'=>'Bỏ qua: Ngày sinh không hợp lệ',
                                'ok'=>false
                            ];
                            continue;
                        }

                        $birthDate = sprintf('%04d-%02d-%02d', $year, $month, $day);
                    } elseif (preg_match('/^(\\d{4})-(\\d{1,2})-(\\d{1,2})$/', $birthDateRaw, $m)) {
                        $year = (int)$m[1];
                        $month = (int)$m[2];
                        $day = (int)$m[3];

                        if (!checkdate($month, $day, $year)) {
                            $bulkCreateResults[] = [
                                'name'=>$fullName,
                                'login'=>$phone!==''?$phone:$email,
                                'password'=>'',
                                'status'=>'Bỏ qua: Ngày sinh không hợp lệ',
                                'ok'=>false
                            ];
                            continue;
                        }

                        $birthDate = sprintf('%04d-%02d-%02d', $year, $month, $day);
                    } elseif (is_numeric($birthDateRaw)) {
                        $serial = (float)$birthDateRaw;
                        if ($serial > 0) {
                            $days = (int)floor($serial);
                            $timestamp = strtotime('1899-12-30 +' . $days . ' days');
                            if ($timestamp !== false) {
                                $birthDate = date('Y-m-d', $timestamp);
                            }
                        }

                        if ($birthDate === '') {
                            $bulkCreateResults[] = [
                                'name'=>$fullName,
                                'login'=>$phone!==''?$phone:$email,
                                'password'=>'',
                                'status'=>'Bỏ qua: Ngày sinh Excel không hợp lệ',
                                'ok'=>false
                            ];
                            continue;
                        }
                    } else {
                        $bulkCreateResults[] = [
                            'name'=>$fullName,
                            'login'=>$phone!==''?$phone:$email,
                            'password'=>'',
                            'status'=>'Bỏ qua: Ngày sinh phải theo dạng dd/mm/yyyy',
                            'ok'=>false
                        ];
                        continue;
                    }
                }

                // Chuẩn hóa giới tính từ file Excel về giá trị database.
                // File mẫu có thể nhập: Nam / Nữ, male / female, M / F.
                $genderRaw = mb_strtolower(trim($get('gender')), 'UTF-8');
                $gender = '';
                if ($genderRaw !== '') {
                    if (in_array($genderRaw, ['nam', 'male', 'm'], true)) {
                        $gender = 'male';
                    } elseif (in_array($genderRaw, ['nữ', 'nu', 'female', 'f'], true)) {
                        $gender = 'female';
                    } else {
                        $bulkCreateResults[] = [
                            'name'=>$fullName,
                            'login'=>$phone!==''?$phone:$email,
                            'password'=>'',
                            'status'=>'Bỏ qua: Giới tính chỉ nhập Nam hoặc Nữ',
                            'ok'=>false
                        ];
                        continue;
                    }
                }

                $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'=>$birthDate,'gender'=>$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) {
                    $dupSql = "SELECT id FROM students WHERE (" . implode(' OR ', $duplicateWhere) . ")";
                    $dupSql .= esTeacherStudentScopeSql(
                        $currentUserRole,
                        $teacherScopeReady,
                        $studentColumns,
                        $teacherScopeProvinceId,
                        $teacherScopeSchoolId
                    );
                    $duplicateParams += esTeacherStudentScopeParams(
                        $currentUserRole,
                        $teacherScopeReady,
                        $studentColumns,
                        $teacherScopeProvinceId,
                        $teacherScopeSchoolId,
                        $teacherScopeProvince,
                        $teacherScopeSchool
                    );
                    $dupSql .= " LIMIT 1";
                    $check = $pdo->prepare($dupSql);
                    $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;}

                // Teacher không nhập Tỉnh/Trường trong Excel.
                // Hệ thống tự gắn học sinh vào đúng phạm vi teacher_profiles.
                if ($currentUserRole === 'teacher') {
                    $addField('province_id', $teacherScopeProvinceId);
                    $addField('school_id', $teacherScopeSchoolId);
                    $addField('province', $teacherScopeProvince);
                    $addField('school_name', $teacherScopeSchool);
                } elseif ($currentUserRole === 'admin') {
                    // Admin chọn Tỉnh/Trường trên trang import; toàn bộ học sinh trong file được gắn vào trường này.
                    $addField('province_id', $postAdminProvinceId);
                    $addField('school_id', $postAdminSchoolId);
                    $addField('province', $adminProvinceName);
                    $addField('school_name', $adminSchoolName);
                }

                if(isset($studentColumns['status']))$addField('status','active');
                if($hasAcademicYearId && $postAcademicYearId>0)$addField('academic_year_id',$postAcademicYearId);
                $stmt=$pdo->prepare("INSERT INTO students (".implode(', ',$insertCols).") VALUES (".implode(', ',$insertMarks).")");
                $stmt->execute($insertParams);
                $newStudentId = (int)$pdo->lastInsertId();

                // Tự động gắn học sinh mới với referral của giáo viên đang import.
                // Không cần cột referral trong file Excel và không cần giáo viên nhập lại mã.
                if (
                    $currentUserRole === 'teacher'
                    && $currentUserId > 0
                    && $newStudentId > 0
                    && $referralTablesReady
                    && $teacherReferralCode !== ''
                ) {
                    $studentReferralStmt = $pdo->prepare(
                        "INSERT IGNORE INTO student_referrals
                            (student_id, teacher_id, referral_code_used, referred_at)
                         VALUES
                            (:student_id, :teacher_id, :referral_code_used, NOW())"
                    );
                    $studentReferralStmt->execute([
                        'student_id' => $newStudentId,
                        'teacher_id' => $currentUserId,
                        'referral_code_used' => $teacherReferralCode,
                    ]);
                }

                $createdCount++;
                $bulkCreateResults[] = [
                    'name' => $row['full_name'],
                    'grade' => $row['grade'] ?? '',
                    'class_name' => $row['class_name'] ?? '',
                    'login' => $row['phone'] !== '' ? $row['phone'] : $row['email'],
                    'password' => $temporaryPassword,
                    'status' => 'Đã import',
                    'ok' => true
                ];
            }
            $pdo->commit();

            $exportRows = array_values(array_filter(
                $bulkCreateResults,
                static fn(array $item): bool => !empty($item['ok']) && !empty($item['password'])
            ));
            if ($exportRows !== []) {
                $_SESSION['student_import_export_rows'] = $exportRows;
                $_SESSION['student_import_export_token'] = bin2hex(random_bytes(24));
            } else {
                unset($_SESSION['student_import_export_rows'], $_SESSION['student_import_export_token']);
            }

            $flashSuccess="Đã import {$createdCount} học sinh".($skippedCount>0?", bỏ qua {$skippedCount} dòng.":'.');
            $_SESSION['teacher_csrf_token']=bin2hex(random_bytes(32));
        } catch (Throwable $e) {
            if ($pdo->inTransaction()) $pdo->rollBack();
            error_log('Teacher import students error: '.$e->getMessage());
            $flashError=$e instanceof RuntimeException?$e->getMessage():'Không thể import danh sách học sinh.';
        }
    } elseif ($action === 'assign_students_class') {
        $selectedIds = $_POST['student_ids'] ?? [];
        $targetClass = trim((string)($_POST['assign_class'] ?? ''));
        $targetGrade = $hasGrade ? max(1, min(5, (int)($_POST['assign_grade'] ?? 0))) : null;

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

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

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

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

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

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

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

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

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

        try {
            if ($studentId <= 0) {
                throw new RuntimeException('Học sinh không hợp lệ.');
            }
            $stmt = $pdo->prepare(
                "UPDATE students
                 SET status = :status" . esTouchSql($hasUpdatedAt) . "
                 WHERE id = :id"
            );
            $stmt->execute(['status' => $newStatus, 'id' => $studentId]);
            $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" : "")
            );
            $params = ['class_name' => $sourceClass];
            if ($hasAcademicYearId && $postAcademicYearId > 0) $params['academic_year_id'] = $postAcademicYearId;
            $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" : "")
            );
            $params = ['class_name' => $sourceClass];
            if ($hasAcademicYearId && $postAcademicYearId > 0) $params['academic_year_id'] = $postAcademicYearId;
            $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;
                }

                $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" : "")
            );
            $countParams = ['class_name' => $sourceClass];
            if ($hasAcademicYearId && $postAcademicYearId > 0) $countParams['academic_year_id'] = $postAcademicYearId;
            $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" : "")
            );
            $updateParams = [
                'grade' => $targetGrade,
                'target_class' => $targetClass,
                'source_class' => $sourceClass,
            ];
            if ($hasAcademicYearId && $postAcademicYearId > 0) $updateParams['academic_year_id'] = $postAcademicYearId;
            $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_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
                 FOR UPDATE"
            );
            $stmt->execute(['request_id' => $requestId]);
            $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.';
        }
    }
}

$pendingRequests = $pdo->query(
    "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'
     ORDER BY r.requested_at ASC"
)->fetchAll(PDO::FETCH_ASSOC);

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

$students = $pdo->query(
    "SELECT " . implode(', ', $studentSelect) . "
     FROM students
     ORDER BY status = 'active' DESC, " .
        ($hasGrade ? "grade ASC, " : "") .
        ($hasClassName ? "class_name ASC, " : "") .
        "full_name ASC, id ASC"
)->fetchAll(PDO::FETCH_ASSOC);

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

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


if (isset($_GET['export_accounts']) && $_GET['export_accounts'] === '1') {
    $exportToken = (string)($_GET['token'] ?? '');
    $sessionToken = (string)($_SESSION['student_import_export_token'] ?? '');

    if ($exportToken === '' || $sessionToken === '' || !hash_equals($sessionToken, $exportToken)) {
        http_response_code(403);
        exit('Liên kết xuất danh sách không hợp lệ hoặc đã hết hạn.');
    }

    $rowsToExport = $_SESSION['student_import_export_rows'] ?? [];
    if (!is_array($rowsToExport) || $rowsToExport === []) {
        http_response_code(404);
        exit('Không còn danh sách tài khoản để xuất.');
    }

    $filename = 'tai-khoan-hoc-sinh-' . date('Ymd-His') . '.csv';

    header('Content-Type: text/csv; charset=UTF-8');
    header('Content-Disposition: attachment; filename="' . $filename . '"');
    header('Pragma: no-cache');
    header('Expires: 0');

    $out = fopen('php://output', 'wb');
    fwrite($out, "\xEF\xBB\xBF");
    fputcsv($out, ['Họ tên', 'Khối', 'Lớp', 'Tài khoản đăng nhập', 'Mật khẩu tạm']);

    foreach ($rowsToExport as $row) {
        fputcsv($out, [
            (string)($row['name'] ?? ''),
            (string)($row['grade'] ?? ''),
            (string)($row['class_name'] ?? ''),
            (string)($row['login'] ?? ''),
            (string)($row['password'] ?? ''),
        ]);
    }

    fclose($out);

    // Chỉ cho tải một lần để tránh mật khẩu tạm lưu lâu trong session.
    unset($_SESSION['student_import_export_rows'], $_SESSION['student_import_export_token']);
    exit;
}

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{border:0;border-radius:10px;padding:9px 11px;font-weight:800;cursor:pointer;white-space:nowrap}
.btn-promote{background:#2563eb;color:#fff}
.btn-archive{background:#fff2e8;color:#b45309;border:1px solid #fed7aa}
.btn-restore{background:#ecfdf3;color:#166534;border:1px solid #bbf7d0}
.student-actions{display:flex;gap:6px;align-items:center}
.student-actions form{margin:0}
.bulk-create-box{margin:18px 20px;border:1px solid #dce7f4;border-radius:14px;background:#fbfdff;overflow:hidden}
.bulk-create-head{padding:15px 16px;border-bottom:1px solid #e8eef5}
.bulk-create-head h3{margin:0 0 5px;color:#17324d;font-size:16px}
.bulk-create-head p{margin:0;color:#748398;font-size:12.5px;line-height:1.45}
.bulk-create-body{padding:15px 16px}
.bulk-create-body textarea{width:100%;min-height:150px;resize:vertical;border:1px solid #dbe3ee;border-radius:11px;padding:11px 12px;background:#fff;font:inherit;line-height:1.5}
.bulk-create-help{margin-top:8px;color:#64748b;font-size:12px;line-height:1.45}
.bulk-create-actions{display:flex;align-items:center;justify-content:space-between;gap:10px;flex-wrap:wrap;margin-top:10px}
.bulk-create-result{margin:0 20px 18px;border:1px solid #dbe7f2;border-radius:12px;overflow:auto}
.bulk-create-result table{width:100%;border-collapse:collapse;min-width:680px}
.bulk-create-result th,.bulk-create-result td{padding:10px 12px;border-bottom:1px solid #edf1f6;text-align:left;font-size:12.5px}
.bulk-create-result th{background:#f8fafc;color:#617085}
.bulk-create-result tr:last-child td{border-bottom:0}
.bulk-ok{color:#166534;font-weight:800}
.bulk-skip{color:#9a3412;font-weight:700}
.password-cell{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-weight:800;color:#0d67c6}
.filter-bar{display:flex;align-items:end;gap:10px;flex-wrap:wrap;padding:14px 20px;border-bottom:1px solid #edf1f6;background:#fff}
.filter-bar label{display:grid;gap:5px;color:#617085;font-size:12px;font-weight:700}
.filter-bar input,.filter-bar select{min-height:40px;border:1px solid #dbe3ee;border-radius:10px;padding:8px 10px;background:#fff;font:inherit;outline:none}
.filter-bar input{min-width:260px}
.filter-bar select{min-width:130px}
.filter-bar input:focus,.filter-bar select:focus{border-color:#80aef8;box-shadow:0 0 0 3px rgba(37,99,235,.10)}
.filter-caption{width:100%;font-size:12.5px;font-weight:800;color:#334155;margin-bottom:-2px}
.assign-bar{display:flex;align-items:end;gap:10px;flex-wrap:wrap;padding:14px 20px;border-bottom:1px solid #edf1f6;background:#f8fbff}
.assign-bar label{display:grid;gap:5px;color:#617085;font-size:12px;font-weight:700}
.assign-bar select,.assign-bar input{min-height:40px;border:1px solid #dbe3ee;border-radius:10px;padding:8px 10px;background:#fff;font:inherit}
.assign-bar select{min-width:120px}
.assign-bar input{min-width:190px}
.assign-caption{width:100%;font-size:12.5px;font-weight:800;color:#1e3a5f;margin-bottom:-2px}
.assign-count{align-self:center;margin-left:auto;color:#53657b;font-size:12.5px;font-weight:750}
.assign-btn{min-height:40px;border:0;border-radius:10px;padding:9px 13px;background:#2563eb;color:#fff;font-weight:800;cursor:pointer}
.student-check{width:18px;height:18px;cursor:pointer}
@media(max-width:900px){.year-tools{grid-template-columns:1fr}.assign-count{margin-left:0;width:100%}.filter-bar input{min-width:180px}}
.empty-state{padding:28px 20px;text-align:center;color:#7b8899}
@media(max-width:800px){.reset-grid{grid-template-columns:1fr}.students-heading{flex-direction:column}}

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

</style>

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


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




    <section class="panel">
        <div class="panel-head">
            <div>
                <h2>📥 Import học sinh</h2>
                <p>Tải tệp mẫu, điền danh sách học sinh rồi import vào hệ thống.</p>
            </div>
        </div>

        <div class="bulk-create-box">
            <div class="bulk-create-head">
                <h3>Danh sách học sinh từ Excel</h3>
                <p>Hỗ trợ Excel (.xlsx) hoặc CSV. Học sinh trùng SĐT/Gmail sẽ được bỏ qua, không ghi đè dữ liệu cũ.</p>
            </div>
            <div class="bulk-create-body">
                <?php if ($currentUserRole === 'teacher'): ?>
                    <div style="margin-bottom:12px;padding:10px 12px;border:1px solid #bfdbfe;border-radius:10px;background:#eff6ff;color:#1e40af;font-size:12.5px;">
                        🏫 Học sinh import sẽ tự động gắn vào
                        <strong><?= htmlspecialchars($teacherScopeSchool !== '' ? $teacherScopeSchool : 'Chưa xác định trường', ENT_QUOTES, 'UTF-8') ?></strong>
                        <?= $teacherScopeProvince !== '' ? ' • ' . htmlspecialchars($teacherScopeProvince, ENT_QUOTES, 'UTF-8') : '' ?>.
                        Không cần nhập Tỉnh/Trường trong Excel.
                    </div>
                <?php endif; ?>

                <form method="post" enctype="multipart/form-data" onsubmit="return confirm('Import danh sách 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="academic_year_id" value="<?= $selectedAcademicYearId ?>">
                    <input type="hidden" name="action" value="import_students">

                    <?php if ($currentUserRole === 'admin'): ?>
                        <div style="display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin-bottom:14px;">
                            <label style="display:grid;gap:6px;font-size:12.5px;font-weight:800;color:#52647b;">
                                Tỉnh
                                <select id="adminImportProvince" name="admin_province_id" required
                                        style="min-height:42px;border:1px solid #dbe3ee;border-radius:10px;padding:8px 10px;background:#fff;font:inherit;">
                                    <option value="">-- Chọn Tỉnh --</option>
                                    <?php foreach ($adminProvinceOptions as $province): ?>
                                        <option value="<?= (int)$province['id'] ?>">
                                            <?= htmlspecialchars((string)$province['label'], ENT_QUOTES, 'UTF-8') ?>
                                        </option>
                                    <?php endforeach; ?>
                                </select>
                            </label>

                            <label style="display:grid;gap:6px;font-size:12.5px;font-weight:800;color:#52647b;">
                                Trường
                                <select id="adminImportSchool" name="admin_school_id" required
                                        style="min-height:42px;border:1px solid #dbe3ee;border-radius:10px;padding:8px 10px;background:#fff;font:inherit;">
                                    <option value="">-- Chọn Trường --</option>
                                    <?php foreach ($adminSchoolOptions as $school): ?>
                                        <option value="<?= (int)$school['id'] ?>"
                                                data-province-id="<?= (int)($school['province_id'] ?? 0) ?>">
                                            <?= htmlspecialchars((string)$school['label'], ENT_QUOTES, 'UTF-8') ?>
                                        </option>
                                    <?php endforeach; ?>
                                </select>
                            </label>
                        </div>
                        <div style="margin:-4px 0 14px;color:#64748b;font-size:12.5px;">
                            Toàn bộ học sinh trong file sẽ được gắn vào Tỉnh/Trường đã chọn. Không cần thêm cột Tỉnh/Trường trong Excel.
                        </div>
                    <?php endif; ?>

                    <div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin-bottom:12px;">
                        <a href="mau_import_hoc_sinh.xlsx" download
                           style="display:inline-flex;align-items:center;gap:6px;min-height:40px;padding:9px 13px;border:1px solid #bfdbfe;border-radius:10px;background:#eff6ff;color:#1d4ed8;font-weight:800;text-decoration:none;">
                            ⬇️ Tải tệp mẫu
                        </a>
                        <span class="muted">Điền theo đúng các cột trong tệp mẫu rồi lưu lại.</span>
                    </div>

                    <input type="file" name="student_file" accept=".xlsx,.csv" required
                           style="width:100%;padding:14px;border:1px dashed #b8c8dc;border-radius:11px;background:#fff;">

                    <div class="bulk-create-help">
                        Các cột hỗ trợ: <strong>Họ tên, Ngày sinh, Giới tính, SĐT, Gmail, Khối, Lớp</strong>.
                        Bắt buộc có Họ tên và ít nhất SĐT hoặc Gmail. File .xlsx dùng Sheet1.
                    </div>

                    <div class="bulk-create-actions">
                        <span class="muted">Tài khoản mới mặc định Active.</span>
                        <button type="submit" class="assign-btn">📥 Import học sinh</button>
                    </div>
                </form>
            </div>
        </div>

        <?php if ($bulkCreateResults !== []): ?>
        <div class="bulk-create-result">
            <table>
                <thead>
                <tr>
                    <th>Học sinh</th>
                    <th>Tài khoản đăng nhập</th>
                    <th>Mật khẩu tạm</th>
                    <th>Kết quả</th>
                </tr>
                </thead>
                <tbody>
                <?php foreach ($bulkCreateResults as $result): ?>
                <tr>
                    <td><?= htmlspecialchars((string)$result['name'], ENT_QUOTES, 'UTF-8') ?></td>
                    <td><?= htmlspecialchars((string)$result['login'], ENT_QUOTES, 'UTF-8') ?></td>
                    <td class="password-cell"><?= htmlspecialchars((string)$result['password'], ENT_QUOTES, 'UTF-8') ?></td>
                    <td class="<?= !empty($result['ok']) ? 'bulk-ok' : 'bulk-skip' ?>">
                        <?= htmlspecialchars((string)$result['status'], ENT_QUOTES, 'UTF-8') ?>
                    </td>
                </tr>
                <?php endforeach; ?>
                </tbody>
            </table>
        </div>

        <?php if (!empty($_SESSION['student_import_export_rows']) && !empty($_SESSION['student_import_export_token'])): ?>
            <div style="display:flex;justify-content:flex-end;padding:0 20px 18px;">
                <a
                    href="import.php?export_accounts=1&amp;token=<?= urlencode((string)$_SESSION['student_import_export_token']) ?>"
                    style="display:inline-flex;align-items:center;gap:7px;min-height:40px;padding:9px 14px;border-radius:10px;background:#16a34a;color:#fff;font-weight:800;text-decoration:none;"
                >
                    📥 Xuất danh sách tài khoản
                </a>
            </div>
        <?php endif; ?>

        <?php endif; ?>
    </section>

</div>

<script>
(function () {
    const province = document.getElementById('adminImportProvince');
    const school = document.getElementById('adminImportSchool');
    if (province && school) {
        const options = Array.from(school.options);
        function filterSchools() {
            const provinceId = province.value;
            school.value = '';
            options.forEach(function (option, index) {
                if (index === 0) {
                    option.hidden = false;
                    return;
                }
                const schoolProvinceId = option.dataset.provinceId || '';
                option.hidden = provinceId !== '' && schoolProvinceId !== '' && schoolProvinceId !== '0' && schoolProvinceId !== provinceId;
            });
        }
        province.addEventListener('change', filterSchools);
        filterSchools();
    }
})();

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

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

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

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

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

        refreshCount();
    }

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

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

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

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

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

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

    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>
