(function () {
    // Screens
    var screenWelcome = document.getElementById('screen-welcome');
    var screenLogin = document.getElementById('screen-login');
    var screenRegisterNisn = document.getElementById('screen-register-nisn');
    var screenRegisterPin = document.getElementById('screen-register-pin');
    var screenForgotPin = document.getElementById('screen-forgot-pin');
    var screenHome = document.getElementById('screen-home');

    // Header & settings
    var btnOpenSettings = document.getElementById('btn-open-settings');
    var settingsModal = document.getElementById('settings-modal');
    var btnCloseSettings = document.getElementById('btn-close-settings');
    var backendUrlInput = document.getElementById('backend-url');
    var btnSaveConfig = document.getElementById('btn-save-config');
    var configStatus = document.getElementById('config-status');

    // Registration step 1
    var regNisnInput = document.getElementById('reg-nisn');
    var btnCheckNisn = document.getElementById('btn-check-nisn');
    var regNisnStatus = document.getElementById('reg-nisn-status');

    // Registration step 2
    var regStudentSummary = document.getElementById('reg-student-summary');
    var regClassSelect = document.getElementById('reg-class');
    var regPinInput = document.getElementById('reg-pin');
    var regPin2Input = document.getElementById('reg-pin2');
    var btnCompleteRegister = document.getElementById('btn-complete-register');
    var regPinStatus = document.getElementById('reg-pin-status');

    // Login
    var loginNisnInput = document.getElementById('login-nisn');
    var loginPinInput = document.getElementById('login-pin');
    var btnLogin = document.getElementById('btn-login');
    var loginStatus = document.getElementById('login-status');

    var forgotNisnInput = document.getElementById('forgot-nisn');
    var forgotNewPinInput = document.getElementById('forgot-new-pin');
    var forgotNewPin2Input = document.getElementById('forgot-new-pin2');
    var forgotPinStatus = document.getElementById('forgot-pin-status');
    var btnResetPin = document.getElementById('btn-reset-pin');

    // Home screen
    var homeStudentSummary = document.getElementById('home-student-summary');
    var btnAbsenMasuk = document.getElementById('btn-absen-masuk');
    var btnAbsenPulang = document.getElementById('btn-absen-pulang');
    var btnRefreshLocation = document.getElementById('btn-refresh-location');
    var locationStatus = document.getElementById('location-status');
    var resultBox = document.getElementById('result-box');
    var resultStatus = document.getElementById('result-status');
    var resultMessage = document.getElementById('result-message');
    var resultIcon = document.getElementById('result-icon');
    var resultCloseBtn = document.getElementById('result-close-btn');
    var resultMeta = document.getElementById('result-meta');
    var masukStatusEl = document.getElementById('masuk-status');
    var pulangStatusEl = document.getElementById('pulang-status');
    var homeDateLabel = document.getElementById('home-date-label');
    var currentCheckinType = null;
    var pendingMasukPulangType = null;

    var toastContainer = document.getElementById('toast-container');

    var currentLat = null;
    var currentLng = null;
    var currentStudentProfile = null;

    var cameraModal = document.getElementById('camera-modal');
    var cameraVideo = document.getElementById('camera-video');
    var cameraError = document.getElementById('camera-error');
    var cameraFaceStatus = document.getElementById('camera-face-status');
    var cameraBtnCancel = document.getElementById('camera-modal-close');
    var cameraBtnCancel2 = document.getElementById('camera-btn-cancel');
    var cameraStream = null;
    var faceDetectIntervalId = null;
    var faceDetectConsecutiveCount = 0;
    var faceDetectSubmitting = false;

    var scanQrModal = document.getElementById('scan-qr-modal');
    var scanQrClose = document.getElementById('scan-qr-close');
    var scanQrStatus = document.getElementById('scan-qr-status');
    var qrReader = document.getElementById('qr-reader');
    var qrPinModal = document.getElementById('qr-pin-modal');
    var qrPinClose = document.getElementById('qr-pin-close');
    var qrPinBackdrop = document.getElementById('qr-pin-backdrop');
    var qrPinInput = document.getElementById('qr-pin-input');
    var qrPinCancel = document.getElementById('qr-pin-cancel');
    var qrPinSubmit = document.getElementById('qr-pin-submit');
    var pendingQrToken = null;
    var html5QrCodeInstance = null;

    // Enroll wajah saat daftar
    var enrollFaceModal = document.getElementById('enroll-face-modal');
    var enrollFaceVideo = document.getElementById('enroll-face-video');
    var enrollFaceStatus = document.getElementById('enroll-face-status');
    var enrollFaceError = document.getElementById('enroll-face-error');
    var enrollFaceStart = document.getElementById('enroll-face-start');
    var enrollFaceBackdrop = document.getElementById('enroll-face-backdrop');
    var enrollStream = null;
    var enrollIntervalId = null;
    var enrollCollectedImages = [];
    var enrollTargetCount = 5;
    var enrollMinCount = 3;
    var enrollSubmitting = false;

    // Device credentials — bisa diisi di Pengaturan, default untuk development
    var DEFAULT_DEVICE_CODE = 'MOBILE_APP';
    var DEFAULT_API_KEY = 'MOBILE_APP_SECRET';

    function showToast(message, type) {
        if (!toastContainer) return;
        type = type || 'info';
        var el = document.createElement('div');
        el.className = 'toast ' + (type === 'error' ? 'toast-error' : type === 'success' ? 'toast-success' : 'toast-info');
        el.textContent = message;
        toastContainer.appendChild(el);
        setTimeout(function () {
            el.remove();
        }, 3000);
    }

    function loadConfig() {
        try {
            var raw = localStorage.getItem('sman1_device_config');
            if (!raw) return;
            var cfg = JSON.parse(raw);
            if (cfg.backendUrl) backendUrlInput.value = cfg.backendUrl;
        } catch (e) {
            // ignore
        }
    }

    function saveConfig() {
        var backendUrl = backendUrlInput.value.trim();

        if (!backendUrl) {
            configStatus.textContent = 'Backend URL wajib diisi.';
            configStatus.style.color = '#f97316';
            showToast('Backend URL belum diisi', 'error');
            return;
        }

        var cfg = {
            backendUrl: backendUrl
        };
        try {
            localStorage.setItem('sman1_device_config', JSON.stringify(cfg));
            configStatus.textContent = 'Konfigurasi disimpan.';
            configStatus.style.color = '#22c55e';
            showToast('Konfigurasi disimpan', 'success');
            testConnection();
        } catch (e) {
            configStatus.textContent = 'Gagal menyimpan konfigurasi di browser.';
            configStatus.style.color = '#f97316';
        }
    }

    function testConnection() {
        var url = backendBase() + '/api/mobile/ping';
        configStatus.textContent = 'Mengecek koneksi…';
        configStatus.style.color = '#64748b';
        fetch(url, { method: 'GET', headers: { 'Accept': 'application/json' } })
            .then(function (res) { return res.json(); })
            .then(function (data) {
                configStatus.textContent = 'Server OK — ' + (data.message || 'Terhubung');
                configStatus.style.color = '#22c55e';
                showToast('Server terhubung', 'success');
            })
            .catch(function (err) {
                var msg = err && err.message ? err.message : 'Tidak bisa terhubung';
                configStatus.textContent = 'Gagal: ' + msg;
                configStatus.style.color = '#dc2626';
                showToast('Gagal: ' + msg, 'error');
            });
    }

    function openSettingsModal() {
        settingsModal.classList.remove('hidden');
    }

    function closeSettingsModal() {
        settingsModal.classList.add('hidden');
    }

    function updateLocationStatus(text, isError) {
        locationStatus.textContent = 'Lokasi: ' + text;
        locationStatus.style.borderColor = isError ? 'rgba(220,38,38,0.5)' : 'rgba(148,163,184,0.4)';
        locationStatus.style.color = isError ? '#fecaca' : '#9ca3af';
    }

    function getLocation() {
        if (!navigator.geolocation) {
            updateLocationStatus('Geolocation tidak didukung di perangkat ini', true);
            showToast('Geolocation tidak didukung', 'error');
            return;
        }
        updateLocationStatus('Mengambil lokasi…', false);
        navigator.geolocation.getCurrentPosition(
            function (pos) {
                currentLat = pos.coords.latitude;
                currentLng = pos.coords.longitude;
                updateLocationStatus(currentLat.toFixed(6) + ', ' + currentLng.toFixed(6), false);
            },
            function (err) {
                currentLat = null;
                currentLng = null;
                updateLocationStatus('Gagal mengambil lokasi: ' + err.message, true);
                showToast('Gagal mengambil lokasi', 'error');
            },
            {
                enableHighAccuracy: true,
                timeout: 8000,
                maximumAge: 0
            }
        );
    }

    function setResult(statusCode, message, metaText) {
        resultBox.classList.remove('hidden');
        resultBox.classList.remove('success', 'warning', 'danger');
        var type = 'danger';
        var upper = (statusCode || '').toUpperCase();
        if (upper === 'PRESENT') type = 'success';
        else if (upper === 'LATE') type = 'warning';
        resultBox.classList.add(type);
        resultStatus.className = 'result-popup-status ' + type;
        resultStatus.textContent = upper || '-';
        resultMessage.textContent = message || '';
        if (resultIcon) {
            resultIcon.className = 'result-popup-icon';
            resultIcon.textContent = type === 'success' ? '✓' : type === 'warning' ? '⚠' : '✕';
        }
        if (resultMeta) resultMeta.textContent = metaText || '';
    }

    function backendBase() {
        var backendUrl = backendUrlInput.value.trim();
        return backendUrl.replace(/\/+$/, '');
    }

    function ensureConfig() {
        var backendUrl = backendUrlInput.value.trim();
        if (!backendUrl) {
            showToast('Isi URL server SMAN 1 Garut terlebih dahulu.', 'error');
            openSettingsModal();
            return false;
        }
        return true;
    }

    function renderStudentSummary(container, profile) {
        if (!profile) {
            container.textContent = '';
            return;
        }
        container.innerHTML =
            '<div><strong>' + (profile.name || '-') + '</strong></div>' +
            '<div>NISN: ' + (profile.nisn || '-') + '</div>' +
            '<div>Kelas: ' + (profile.class_label || '-') + '</div>';
        if (container === homeStudentSummary) {
            var greetingEl = document.getElementById('home-greeting-text');
            if (greetingEl && profile.name) {
                var firstName = profile.name.split(/\s+/)[0] || profile.name;
                greetingEl.textContent = 'Halo, ' + firstName;
            }
        }
    }

    function goToScreen(name) {
        screenWelcome.classList.add('hidden');
        screenLogin.classList.add('hidden');
        screenRegisterNisn.classList.add('hidden');
        screenRegisterPin.classList.add('hidden');
        if (screenForgotPin) screenForgotPin.classList.add('hidden');
        screenHome.classList.add('hidden');
        if (name === 'welcome') {
            screenWelcome.classList.remove('hidden');
        } else if (name === 'login') {
            screenLogin.classList.remove('hidden');
        } else if (name === 'register_nisn') {
            screenRegisterNisn.classList.remove('hidden');
        } else if (name === 'register_pin') {
            screenRegisterPin.classList.remove('hidden');
        } else if (name === 'forgot_pin') {
            if (screenForgotPin) {
                screenForgotPin.classList.remove('hidden');
                if (forgotNisnInput && loginNisnInput) forgotNisnInput.value = loginNisnInput.value.trim();
            }
        } else if (name === 'home') {
            screenHome.classList.remove('hidden');
            setHomeDateLabel();
            fetchTodayStatus();
        }
    }

    function saveStudentProfile(profile) {
        currentStudentProfile = profile;
        try {
            localStorage.setItem('sman1_student_profile', JSON.stringify(profile));
        } catch (e) {
            // ignore
        }
    }

    function loadStudentProfile() {
        try {
            var raw = localStorage.getItem('sman1_student_profile');
            if (!raw) return null;
            return JSON.parse(raw);
        } catch (e) {
            return null;
        }
    }

    function handleCheckNisn() {
        if (!ensureConfig()) return;

        var nisn = regNisnInput.value.trim();
        if (nisn === '') {
            regNisnStatus.textContent = 'NISN wajib diisi.';
            regNisnStatus.style.color = '#f97316';
            return;
        }

        var url = backendBase() + '/api/mobile/register/nisn';
        btnCheckNisn.disabled = true;
        btnCheckNisn.textContent = 'Memeriksa…';
        regNisnStatus.textContent = '';

        fetch(url, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'Accept': 'application/json'
            },
            body: JSON.stringify({ nisn: nisn })
        }).then(function (res) {
            return res.json().then(function (data) {
                return { ok: res.ok, status: res.status, data: data };
            });
        }).then(function (r) {
            btnCheckNisn.disabled = false;
            btnCheckNisn.textContent = 'Lanjutkan';

            if (!r.ok) {
                var msg = (r.data && r.data.message) ? r.data.message : 'NISN tidak valid';
                regNisnStatus.textContent = msg;
                regNisnStatus.style.color = '#f97316';
                showToast(msg, 'error');
                if (r.status === 409 || (msg && msg.indexOf('sudah terdaftar') !== -1)) {
                    if (loginNisnInput) loginNisnInput.value = nisn;
                    goToScreen('login');
                }
                return;
            }

            var d = r.data && r.data.data ? r.data.data : r.data;
            if (!d || !d.student_id) {
                regNisnStatus.textContent = 'Response tidak valid dari server.';
                regNisnStatus.style.color = '#f97316';
                return;
            }

            // Isi summary + dropdown kelas
            var summaryProfile = {
                student_id: d.student_id,
                name: d.name,
                nisn: d.nisn,
                class_id: d.current_class_id,
                class_label: d.current_class_label
            };
            renderStudentSummary(regStudentSummary, summaryProfile);

            // Isi dropdown kelas
            var list = Array.isArray(d.available_classes) ? d.available_classes : [];
            var first = regClassSelect.options[0];
            regClassSelect.innerHTML = '';
            if (first) regClassSelect.appendChild(first);
            list.forEach(function (c) {
                var opt = document.createElement('option');
                opt.value = c.id;
                opt.textContent = c.label;
                if (d.current_class_id && c.id === d.current_class_id) {
                    opt.selected = true;
                    summaryProfile.class_label = c.label;
                }
                regClassSelect.appendChild(opt);
            });

            currentStudentProfile = summaryProfile;
            regPinInput.value = '';
            regPin2Input.value = '';
            regPinStatus.textContent = '';

            goToScreen('register_pin');
            showToast('NISN ditemukan. Lengkapi data.', 'success');
        }).catch(function (err) {
            btnCheckNisn.disabled = false;
            btnCheckNisn.textContent = 'Lanjutkan';
            regNisnStatus.textContent = 'Gagal menghubungi server.';
            regNisnStatus.style.color = '#f97316';
            var msg = (err && err.message) ? err.message : 'Gagal menghubungi server';
            showToast(msg, 'error');
        });
    }

    function handleLogin() {
        if (!ensureConfig()) return;

        var nisn = loginNisnInput.value.trim();
        var pin = loginPinInput.value.trim();

        if (nisn === '' || pin === '') {
            loginStatus.textContent = 'NISN dan PIN wajib diisi.';
            loginStatus.style.color = '#f97316';
            return;
        }

        var url = backendBase() + '/api/mobile/login';
        btnLogin.disabled = true;
        btnLogin.textContent = 'Memproses…';
        loginStatus.textContent = '';

        fetch(url, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'Accept': 'application/json'
            },
            body: JSON.stringify({ nisn: nisn, pin: pin })
        }).then(function (res) {
            return res.json().then(function (data) {
                return { ok: res.ok, status: res.status, data: data };
            });
        }).then(function (r) {
            btnLogin.disabled = false;
            btnLogin.textContent = 'Masuk';

            if (!r.ok) {
                var msg = (r.data && r.data.message) ? r.data.message : 'Login gagal';
                loginStatus.textContent = msg;
                loginStatus.style.color = '#f97316';
                showToast(msg, 'error');
                return;
            }

            var d = r.data && r.data.data ? r.data.data : r.data;
            if (!d || !d.student_id) {
                loginStatus.textContent = 'Response tidak valid dari server.';
                loginStatus.style.color = '#f97316';
                return;
            }

            var profile = {
                student_id: d.student_id,
                name: d.name,
                nisn: d.nisn,
                class_id: d.class_id,
                class_label: d.class_label
            };
            saveStudentProfile(profile);
            renderStudentSummary(homeStudentSummary, profile);

            showToast('Login berhasil', 'success');
            goToScreen('home');
        }).catch(function (err) {
            btnLogin.disabled = false;
            btnLogin.textContent = 'Masuk';
            loginStatus.textContent = 'Gagal menghubungi server.';
            loginStatus.style.color = '#f97316';
            var msg = (err && err.message) ? err.message : 'Gagal menghubungi server';
            showToast(msg, 'error');
        });
    }

    function handleCompleteRegister() {
        if (!ensureConfig()) return;
        if (!currentStudentProfile || !currentStudentProfile.student_id) {
            showToast('Mulai dari input NISN dulu.', 'error');
            goToScreen('register_nisn');
            return;
        }

        var classId = parseInt(regClassSelect.value, 10);
        var pin = regPinInput.value.trim();
        var pin2 = regPin2Input.value.trim();

        if (!classId || classId <= 0) {
            regPinStatus.textContent = 'Pilih kelas terlebih dahulu.';
            regPinStatus.style.color = '#f97316';
            return;
        }
        if (pin.length < 4) {
            regPinStatus.textContent = 'PIN minimal 4 digit.';
            regPinStatus.style.color = '#f97316';
            return;
        }
        if (pin !== pin2) {
            regPinStatus.textContent = 'PIN dan ulangi PIN tidak sama.';
            regPinStatus.style.color = '#f97316';
            return;
        }

        var url = backendBase() + '/api/mobile/register/complete';
        btnCompleteRegister.disabled = true;
        btnCompleteRegister.textContent = 'Menyimpan…';
        regPinStatus.textContent = '';

        fetch(url, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'Accept': 'application/json'
            },
            body: JSON.stringify({
                student_id: currentStudentProfile.student_id,
                class_id: classId,
                pin: pin
            })
        }).then(function (res) {
            return res.json().then(function (data) {
                return { ok: res.ok, status: res.status, data: data };
            });
        }).then(function (r) {
            btnCompleteRegister.disabled = false;
            btnCompleteRegister.textContent = 'Simpan & Mulai';

            if (!r.ok) {
                var msg = (r.data && r.data.message) ? r.data.message : 'Gagal menyimpan registrasi';
                regPinStatus.textContent = msg;
                regPinStatus.style.color = '#f97316';
                showToast(msg, 'error');
                return;
            }

            var d = r.data && r.data.data ? r.data.data : r.data;
            if (!d || !d.student_id) {
                regPinStatus.textContent = 'Response tidak valid dari server.';
                regPinStatus.style.color = '#f97316';
                return;
            }

            var profile = {
                student_id: d.student_id,
                name: d.name,
                nisn: d.nisn,
                class_id: d.class_id,
                class_label: d.class_label
            };
            saveStudentProfile(profile);
            renderStudentSummary(homeStudentSummary, profile);

            openEnrollFaceModal();
            showToast('Registrasi berhasil. Rekam wajah untuk verifikasi absen.', 'success');
        }).catch(function (err) {
            btnCompleteRegister.disabled = false;
            btnCompleteRegister.textContent = 'Simpan & Mulai';
            regPinStatus.textContent = 'Gagal menghubungi server.';
            regPinStatus.style.color = '#f97316';
            var msg = (err && err.message) ? err.message : 'Gagal menghubungi server';
            showToast(msg, 'error');
        });
    }

    function handleResetPin() {
        if (!ensureConfig()) return;

        var nisn = forgotNisnInput ? forgotNisnInput.value.trim() : '';
        var newPin = forgotNewPinInput ? forgotNewPinInput.value.trim() : '';
        var newPin2 = forgotNewPin2Input ? forgotNewPin2Input.value.trim() : '';

        if (nisn === '') {
            if (forgotPinStatus) {
                forgotPinStatus.textContent = 'NISN wajib diisi.';
                forgotPinStatus.style.color = '#f97316';
            }
            showToast('NISN wajib diisi', 'error');
            return;
        }
        if (newPin.length < 4) {
            if (forgotPinStatus) {
                forgotPinStatus.textContent = 'PIN baru minimal 4 digit.';
                forgotPinStatus.style.color = '#f97316';
            }
            showToast('PIN baru minimal 4 digit', 'error');
            return;
        }
        if (newPin !== newPin2) {
            if (forgotPinStatus) {
                forgotPinStatus.textContent = 'PIN baru dan ulangi tidak sama.';
                forgotPinStatus.style.color = '#f97316';
            }
            showToast('PIN baru dan ulangi tidak sama', 'error');
            return;
        }

        if (forgotPinStatus) forgotPinStatus.textContent = '';
        var url = backendBase() + '/api/mobile/forgot-pin';
        if (btnResetPin) {
            btnResetPin.disabled = true;
            btnResetPin.textContent = 'Memproses…';
        }

        fetch(url, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },
            body: JSON.stringify({
                nisn: nisn,
                new_pin: newPin,
                new_pin_confirm: newPin2
            })
        }).then(function (res) {
            return res.json().then(function (data) {
                return { ok: res.ok, status: res.status, data: data };
            });
        }).then(function (r) {
            if (btnResetPin) {
                btnResetPin.disabled = false;
                btnResetPin.textContent = 'Reset PIN';
            }
            if (!r.ok) {
                var msg = (r.data && r.data.message) ? r.data.message : 'Gagal reset PIN';
                if (forgotPinStatus) {
                    forgotPinStatus.textContent = msg;
                    forgotPinStatus.style.color = '#f97316';
                }
                showToast(msg, 'error');
                return;
            }
            showToast('PIN berhasil direset. Silakan masuk dengan PIN baru.', 'success');
            if (loginNisnInput) loginNisnInput.value = nisn;
            if (loginPinInput) loginPinInput.value = '';
            goToScreen('login');
        }).catch(function (err) {
            if (btnResetPin) {
                btnResetPin.disabled = false;
                btnResetPin.textContent = 'Reset PIN';
            }
            var msg = (err && err.message) ? err.message : 'Gagal menghubungi server';
            if (forgotPinStatus) {
                forgotPinStatus.textContent = msg;
                forgotPinStatus.style.color = '#f97316';
            }
            showToast(msg, 'error');
        });
    }

    function doCheckin() {
        if (!ensureConfig()) return;
        if (!currentStudentProfile || !currentStudentProfile.student_id) {
            showToast('Lengkapi registrasi siswa dulu.', 'error');
            goToScreen('register_nisn');
            return;
        }
        if (currentLat == null || currentLng == null) {
            showToast('Lokasi belum diambil. Tekan tombol refresh lokasi dulu.', 'error');
            return;
        }
        openCameraModal();
    }

    function getActiveCheckinButton() {
        if (currentCheckinType === 'masuk' && btnAbsenMasuk) return btnAbsenMasuk;
        if (currentCheckinType === 'pulang' && btnAbsenPulang) return btnAbsenPulang;
        return null;
    }

    function fetchTodayStatus() {
        if (!currentStudentProfile || !currentStudentProfile.student_id) return;
        var url = backendBase() + '/api/mobile/attendance/today?student_id=' + encodeURIComponent(currentStudentProfile.student_id);
        fetch(url, { method: 'GET', headers: { 'Accept': 'application/json' } })
            .then(function (res) { return res.json(); })
            .then(function (res) {
                if (res && res.success && res.data) {
                    updatePresenceUI(res.data);
                }
            })
            .catch(function () {});
    }

    function updatePresenceUI(data) {
        var hasMasuk = data.has_masuk === true;
        var hasPulang = data.has_pulang === true;
        if (masukStatusEl && btnAbsenMasuk) {
            if (hasMasuk) {
                masukStatusEl.classList.remove('hidden');
                btnAbsenMasuk.classList.add('hidden');
            } else {
                masukStatusEl.classList.add('hidden');
                btnAbsenMasuk.classList.remove('hidden');
            }
        }
        if (pulangStatusEl && btnAbsenPulang) {
            if (hasPulang) {
                pulangStatusEl.classList.remove('hidden');
                btnAbsenPulang.classList.add('hidden');
            } else {
                pulangStatusEl.classList.add('hidden');
                btnAbsenPulang.classList.remove('hidden');
            }
        }
    }

    function setHomeDateLabel() {
        if (!homeDateLabel) return;
        var d = new Date();
        var days = ['Minggu', 'Senin', 'Selasa', 'Rabu', 'Kamis', 'Jumat', 'Sabtu'];
        var dayName = days[d.getDay()];
        var dateStr = d.getDate() + ' ' + ['Jan','Feb','Mar','Apr','Mei','Jun','Jul','Agu','Sep','Okt','Nov','Des'][d.getMonth()] + ' ' + d.getFullYear();
        homeDateLabel.textContent = dayName + ', ' + dateStr;
    }

    function stopFaceDetection() {
        if (faceDetectIntervalId) {
            clearInterval(faceDetectIntervalId);
            faceDetectIntervalId = null;
        }
        faceDetectConsecutiveCount = 0;
        if (cameraFaceStatus) {
            cameraFaceStatus.classList.add('hidden');
            cameraFaceStatus.textContent = '';
        }
    }

    function openCameraModal() {
        if (!cameraModal || !cameraVideo) return;
        faceDetectSubmitting = false;
        stopFaceDetection();
        cameraModal.classList.remove('hidden');
        if (cameraError) cameraError.classList.add('hidden');
        if (cameraFaceStatus) cameraFaceStatus.classList.add('hidden');
        cameraVideo.style.display = 'block';
        var constraints = { video: { facingMode: 'user', width: { ideal: 640 }, height: { ideal: 480 } }, audio: false };
        navigator.mediaDevices.getUserMedia(constraints).then(function (stream) {
            cameraStream = stream;
            cameraVideo.srcObject = stream;
            cameraVideo.onloadedmetadata = function () {
                cameraVideo.play().then(function () {
                    startFaceDetection();
                }).catch(function () {});
            };
        }).catch(function (err) {
            if (cameraError) {
                cameraError.textContent = 'Kamera tidak tersedia atau izin ditolak.';
                cameraError.classList.remove('hidden');
            }
            cameraVideo.style.display = 'none';
            showToast('Aktifkan izin kamera untuk verifikasi wajah.', 'error');
        });
    }

    function captureCurrentFrameAsBase64() {
        if (!cameraVideo || cameraVideo.readyState < 2) return null;
        var w = cameraVideo.videoWidth || 640;
        var h = cameraVideo.videoHeight || 480;
        if (w === 0 || h === 0) return null;
        var canvas = document.createElement('canvas');
        canvas.width = w;
        canvas.height = h;
        var ctx = canvas.getContext('2d');
        ctx.drawImage(cameraVideo, 0, 0, w, h);
        return canvas.toDataURL('image/jpeg', 0.9);
    }

    function verifyFaceOnServer(imageBase64) {
        if (!ensureConfig()) return Promise.reject(new Error('Config backend belum diisi'));
        if (!currentStudentProfile || !currentStudentProfile.student_id) {
            return Promise.reject(new Error('Profil siswa belum tersedia'));
        }
        var url = backendBase() + '/api/attendance/verify-face';
        return fetch(url, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'Accept': 'application/json'
            },
            body: JSON.stringify({
                student_id: currentStudentProfile.student_id,
                image: imageBase64
            })
        }).then(function (res) {
            return res.json().then(function (data) {
                return { ok: res.ok, status: res.status, data: data };
            });
        }).then(function (r) {
            if (!r.ok || !r.data) {
                var msg = (r.data && r.data.message) ? r.data.message : 'Verifikasi wajah gagal';
                throw new Error(msg);
            }
            var d = r.data.data || r.data;
            if (!d) throw new Error('Response verifikasi tidak valid');
            return d; // { student_id, similarity, threshold, matched_source, status }
        });
    }

    function startFaceDetection() {
        if (faceDetectSubmitting || !cameraVideo || !cameraStream) return;
        if (typeof faceapi === 'undefined') return;

        var isMasukPulang = currentCheckinType === 'masuk' || currentCheckinType === 'pulang';
        var weightsUrl = 'https://cdn.jsdelivr.net/npm/face-api.js@0.22.2/weights/';

        function runDetection() {
            if (faceDetectIntervalId || !cameraStream) return;
            if (cameraFaceStatus) {
                cameraFaceStatus.classList.remove('hidden');
                cameraFaceStatus.classList.add('detecting');
                cameraFaceStatus.textContent = 'Mendeteksi wajah…';
            }
            var requiredConsecutive = 5;
            faceDetectIntervalId = setInterval(function () {
                if (faceDetectSubmitting || !cameraStream || !cameraVideo.srcObject) return;
                if (cameraVideo.readyState < 2) return;
                var detect = faceapi.detectSingleFace(cameraVideo, new faceapi.TinyFaceDetectorOptions({ inputSize: 224, scoreThreshold: 0.5 }));
                detect.then(function (detection) {
                    if (faceDetectSubmitting) return;
                    if (detection) {
                        faceDetectConsecutiveCount++;
                        if (cameraFaceStatus) {
                            cameraFaceStatus.textContent = 'Wajah terdeteksi… ' + (faceDetectConsecutiveCount >= requiredConsecutive ? 'Memverifikasi…' : '');
                        }
                        if (faceDetectConsecutiveCount >= requiredConsecutive) {
                            faceDetectSubmitting = true;
                            stopFaceDetection();
                            if (cameraFaceStatus) {
                                cameraFaceStatus.classList.remove('detecting');
                                cameraFaceStatus.classList.add('sending');
                                cameraFaceStatus.textContent = 'Memverifikasi wajah…';
                            }
                            if (isMasukPulang) {
                                var frameBase64 = captureCurrentFrameAsBase64();
                                if (!frameBase64) {
                                    faceDetectSubmitting = false;
                                    faceDetectConsecutiveCount = 0;
                                    if (cameraFaceStatus) {
                                        cameraFaceStatus.textContent = 'Gagal menangkap frame. Coba lagi.';
                                    }
                                    return;
                                }
                                verifyFaceOnServer(frameBase64).then(function (result) {
                                    if (result.status !== 'match') {
                                        faceDetectSubmitting = false;
                                        faceDetectConsecutiveCount = 0;
                                        if (cameraFaceStatus) {
                                            cameraFaceStatus.textContent = 'Wajah tidak dikenali server. Coba lagi.';
                                        }
                                        showToast('Verifikasi wajah gagal. Coba lagi.', 'error');
                                        return;
                                    }
                                    pendingMasukPulangType = currentCheckinType;
                                    closeCameraModal();
                                    var titleEl = qrPinModal && qrPinModal.querySelector('.modal-panel-title');
                                    if (titleEl) titleEl.textContent = currentCheckinType === 'masuk' ? 'Verifikasi wajah berhasil - Masukkan PIN' : 'Verifikasi wajah berhasil - Masukkan PIN';
                                    var hintEl = qrPinModal && qrPinModal.querySelector('.modal-panel-hint');
                                    if (hintEl) hintEl.textContent = 'Wajah terverifikasi server. Masukkan PIN untuk menyelesaikan absen ' + (currentCheckinType === 'masuk' ? 'masuk' : 'pulang') + '.';
                                    if (qrPinInput) { qrPinInput.value = ''; qrPinInput.focus(); }
                                    if (qrPinModal) qrPinModal.classList.remove('hidden');
                                }).catch(function (err) {
                                    faceDetectSubmitting = false;
                                    faceDetectConsecutiveCount = 0;
                                    if (cameraFaceStatus) {
                                        cameraFaceStatus.textContent = 'Gagal verifikasi wajah di server.';
                                    }
                                    var msg = (err && err.message) ? err.message : 'Gagal verifikasi wajah di server';
                                    showToast(msg, 'error');
                                });
                            } else {
                                doCheckinSubmit();
                            }
                        }
                    } else {
                        faceDetectConsecutiveCount = 0;
                        if (cameraFaceStatus && !faceDetectSubmitting) {
                            cameraFaceStatus.textContent = 'Arahkan wajah ke kamera…';
                        }
                    }
                })
                .catch(function () {
                    faceDetectConsecutiveCount = 0;
                });
            }, 300);
        }

        faceapi.nets.tinyFaceDetector.loadFromUri(weightsUrl).then(runDetection).catch(function () {
            if (cameraFaceStatus) cameraFaceStatus.classList.add('hidden');
        });
    }

    function closeCameraModal() {
        stopFaceDetection();
        stopCamera();
        if (cameraModal) cameraModal.classList.add('hidden');
    }

    function captureEnrollFrameAsBase64() {
        if (!enrollFaceVideo || enrollFaceVideo.readyState < 2) return null;
        var w = enrollFaceVideo.videoWidth || 640;
        var h = enrollFaceVideo.videoHeight || 480;
        if (w === 0 || h === 0) return null;
        var canvas = document.createElement('canvas');
        canvas.width = w;
        canvas.height = h;
        var ctx = canvas.getContext('2d');
        ctx.drawImage(enrollFaceVideo, 0, 0, w, h);
        return canvas.toDataURL('image/jpeg', 0.9);
    }

    function closeEnrollFaceModal() {
        if (enrollIntervalId) {
            clearInterval(enrollIntervalId);
            enrollIntervalId = null;
        }
        enrollCollectedImages = [];
        if (enrollStream) {
            enrollStream.getTracks().forEach(function (t) { t.stop(); });
            enrollStream = null;
        }
        if (enrollFaceVideo) enrollFaceVideo.srcObject = null;
        if (enrollFaceModal) enrollFaceModal.classList.add('hidden');
        if (enrollFaceStatus) {
            enrollFaceStatus.classList.add('hidden');
            enrollFaceStatus.textContent = '';
        }
    }

    function openEnrollFaceModal() {
        if (!ensureConfig()) {
            goToScreen('home');
            return;
        }
        if (!enrollFaceModal || !enrollFaceVideo || !currentStudentProfile || !currentStudentProfile.student_id) {
            goToScreen('home');
            return;
        }
        enrollCollectedImages = [];
        enrollSubmitting = false;
        if (enrollFaceError) enrollFaceError.classList.add('hidden');
        if (enrollFaceStatus) {
            enrollFaceStatus.textContent = 'Menyiapkan kamera…';
            enrollFaceStatus.classList.remove('hidden');
        }
        enrollFaceModal.classList.remove('hidden');
        enrollFaceVideo.style.display = 'block';
        var constraints = { video: { facingMode: 'user', width: { ideal: 640 }, height: { ideal: 480 } }, audio: false };
        navigator.mediaDevices.getUserMedia(constraints).then(function (stream) {
            enrollStream = stream;
            enrollFaceVideo.srcObject = stream;
            enrollFaceVideo.onloadedmetadata = function () {
                enrollFaceVideo.play().then(function () {
                    if (enrollFaceStatus) enrollFaceStatus.textContent = 'Arahkan wajah ke kamera lalu tekan Rekam.';
                }).catch(function () {});
            };
        }).catch(function () {
            if (enrollFaceError) {
                enrollFaceError.textContent = 'Kamera tidak tersedia atau izin ditolak. Bisa dilewati.';
                enrollFaceError.classList.remove('hidden');
            }
            if (enrollFaceStatus) enrollFaceStatus.textContent = 'Kamera gagal. Tekan Lewati untuk lanjut.';
        });
    }

    function startEnrollCapture() {
        if (enrollSubmitting || !enrollStream || !enrollFaceVideo || typeof faceapi === 'undefined') return;
        if (!currentStudentProfile || !currentStudentProfile.student_id) return;
        enrollSubmitting = true;
        enrollCollectedImages = [];
        if (enrollFaceStart) { enrollFaceStart.disabled = true; enrollFaceStart.textContent = 'Merekam…'; }
        var weightsUrl = 'https://cdn.jsdelivr.net/npm/face-api.js@0.22.2/weights/';
        faceapi.nets.tinyFaceDetector.loadFromUri(weightsUrl).then(function () {
            var tick = 0;
            var lastFaceCount = 0;
            enrollIntervalId = setInterval(function () {
                if (!enrollStream || !enrollFaceVideo.srcObject || enrollFaceVideo.readyState < 2) return;
                var detect = faceapi.detectSingleFace(enrollFaceVideo, new faceapi.TinyFaceDetectorOptions({ inputSize: 224, scoreThreshold: 0.5 }));
                detect.then(function (detection) {
                    if (detection && enrollCollectedImages.length < enrollTargetCount) {
                        var b64 = captureEnrollFrameAsBase64();
                        if (b64) {
                            enrollCollectedImages.push(b64);
                            if (enrollFaceStatus) enrollFaceStatus.textContent = 'Rekam ' + enrollCollectedImages.length + '/' + enrollTargetCount + '…';
                        }
                    }
                    if (enrollCollectedImages.length >= enrollMinCount) {
                        clearInterval(enrollIntervalId);
                        enrollIntervalId = null;
                        var imgs = enrollCollectedImages.slice(0, enrollTargetCount);
                        var url = backendBase() + '/api/face/enroll-live';
                        if (enrollFaceStatus) enrollFaceStatus.textContent = 'Mengirim ke server…';
                        fetch(url, {
                            method: 'POST',
                            headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },
                            body: JSON.stringify({ student_id: currentStudentProfile.student_id, images: imgs })
                        }).then(function (r) { return r.json().then(function (d) { return { ok: r.ok, data: d }; }); })
                        .then(function (r) {
                            enrollSubmitting = false;
                            if (enrollFaceStart) { enrollFaceStart.disabled = false; enrollFaceStart.textContent = 'Rekam (3–5 foto)'; }
                            if (r.ok && r.data && (r.data.saved || 0) > 0) {
                                showToast('Wajah berhasil direkam. Siap untuk verifikasi absen.', 'success');
                            } else {
                                showToast('Rekam wajah gagal. Bisa dicoba nanti dari pengaturan.', 'info');
                            }
                            closeEnrollFaceModal();
                            goToScreen('home');
                        }).catch(function () {
                            enrollSubmitting = false;
                            if (enrollFaceStart) { enrollFaceStart.disabled = false; enrollFaceStart.textContent = 'Rekam (3–5 foto)'; }
                            if (enrollFaceStatus) enrollFaceStatus.textContent = 'Gagal mengirim. Coba lagi atau Lewati.';
                            showToast('Gagal mengirim rekaman wajah.', 'error');
                        });
                    }
                }).catch(function () {});
                tick++;
                if (tick > 80) {
                    clearInterval(enrollIntervalId);
                    enrollIntervalId = null;
                    enrollSubmitting = false;
                    if (enrollFaceStart) { enrollFaceStart.disabled = false; enrollFaceStart.textContent = 'Rekam (3–5 foto)'; }
                    if (enrollCollectedImages.length >= 1) {
                        var url = backendBase() + '/api/face/enroll-live';
                        if (enrollFaceStatus) enrollFaceStatus.textContent = 'Mengirim ' + enrollCollectedImages.length + ' foto…';
                        fetch(url, {
                            method: 'POST',
                            headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },
                            body: JSON.stringify({ student_id: currentStudentProfile.student_id, images: enrollCollectedImages })
                        }).then(function (r) { return r.json().then(function (d) { return { ok: r.ok, data: d }; }); })
                        .then(function (r) {
                            if (r.ok && (r.data && r.data.saved > 0)) showToast('Wajah berhasil direkam.', 'success');
                            else showToast('Rekam wajah gagal. Bisa dicoba nanti.', 'info');
                            closeEnrollFaceModal();
                            goToScreen('home');
                        }).catch(function () {
                            enrollSubmitting = false;
                            if (enrollFaceStart) { enrollFaceStart.disabled = false; enrollFaceStart.textContent = 'Rekam (3–5 foto)'; }
                            if (enrollFaceStatus) enrollFaceStatus.textContent = 'Gagal mengirim. Coba lagi atau Lewati.';
                            showToast('Gagal mengirim rekaman wajah.', 'error');
                        });
                    } else {
                        enrollSubmitting = false;
                        if (enrollFaceStart) { enrollFaceStart.disabled = false; enrollFaceStart.textContent = 'Rekam (3–5 foto)'; }
                        if (enrollFaceStatus) enrollFaceStatus.textContent = 'Wajah tidak terdeteksi. Arahkan wajah ke kamera lalu Rekam lagi.';
                    }
                }
            }, 500);
        }).catch(function () {
            enrollSubmitting = false;
            if (enrollFaceStart) { enrollFaceStart.disabled = false; enrollFaceStart.textContent = 'Rekam (3–5 foto)'; }
            if (enrollFaceStatus) enrollFaceStatus.textContent = 'Gagal memuat deteksi wajah. Lewati atau coba lagi.';
        });
    }

    function stopCamera() {
        if (cameraStream) {
            cameraStream.getTracks().forEach(function (t) { t.stop(); });
            cameraStream = null;
        }
        if (cameraVideo) cameraVideo.srcObject = null;
    }

    function doCheckinSubmit() {
        var url = backendBase() + '/api/attendance/checkin';
        var deviceCode = DEFAULT_DEVICE_CODE;
        var apiKey = DEFAULT_API_KEY;
        var payload = {
            device_code: deviceCode,
            api_key: apiKey,
            student_id: currentStudentProfile.student_id,
            datetime: new Date().toISOString().slice(0, 19).replace('T', ' '),
            lat: currentLat,
            lng: currentLng,
            confidence: 1.0
        };

        var activeBtn = getActiveCheckinButton();
        if (activeBtn) {
            activeBtn.disabled = true;
            activeBtn.textContent = 'Memproses…';
        }

        fetch(url, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'Accept': 'application/json'
            },
            body: JSON.stringify(payload)
        })
            .then(function (res) {
                return res.json().then(function (data) {
                    return { ok: res.ok, status: res.status, data: data };
                });
            })
            .then(function (r) {
                if (activeBtn) {
                    activeBtn.disabled = false;
                    activeBtn.textContent = currentCheckinType === 'masuk' ? 'Absen Masuk' : 'Absen Pulang';
                }

                if (!r.ok) {
                    faceDetectSubmitting = false;
                    var msg = (r.data && r.data.message) ? r.data.message : 'Gagal check-in';
                    showToast(msg, 'error');
                    setResult('ERROR', msg, '');
                    return;
                }

                var body = r.data || {};
                var status = body.data && body.data.status ? body.data.status : 'UNKNOWN';
                var meta = '';
                if (body.data && body.data.checkin_at) {
                    meta = 'Waktu server: ' + body.data.checkin_at;
                }

                if (status === 'OUTSIDE_ZONE') {
                    updateLocationStatus('Anda di luar jangkauan yang diizinkan', true);
                }

                var friendly;
                switch (status) {
                    case 'PRESENT':
                        friendly = 'Absensi tercatat sebagai HADIR.';
                        break;
                    case 'LATE':
                        friendly = 'Absensi tercatat, namun TERLAMBAT.';
                        break;
                    case 'OUTSIDE_ZONE':
                        friendly = 'Anda di luar jangkauan yang diizinkan.';
                        break;
                    case 'NO_SCHEDULE':
                        friendly = 'Tidak ada jadwal aktif untuk siswa ini.';
                        break;
                    case 'INVALID_DEVICE':
                        friendly = 'Device code / API key tidak valid.';
                        break;
                    case 'ALREADY_CHECKED_IN':
                        friendly = 'Siswa ini sudah check-in untuk jadwal hari ini.';
                        break;
                    case 'ABSENCE_WINDOW_CLOSED':
                        friendly = 'Check-in di luar window waktu absensi.';
                        break;
                    case 'SESSION_CLOSED':
                        friendly = 'Sesi pelajaran sudah ditutup.';
                        break;
                    default:
                        friendly = body.message || 'Status: ' + status;
                        break;
                }

                setResult(status, friendly, meta);
                showToast('Status: ' + status, (status === 'PRESENT' || status === 'LATE') ? 'success' : 'info');
                currentCheckinType = null;
                fetchTodayStatus();
                closeCameraModal();
            })
            .catch(function (err) {
                if (activeBtn) {
                    activeBtn.disabled = false;
                    activeBtn.textContent = currentCheckinType === 'masuk' ? 'Absen Masuk' : 'Absen Pulang';
                }
                faceDetectSubmitting = false;
                currentCheckinType = null;
                var msg = (err && err.message) ? err.message : 'Tidak dapat terhubung ke backend.';
                showToast(msg, 'error');
                setResult('ERROR', msg, '');
            });
    }

    function openScanQrModal() {
        if (!ensureConfig()) return;
        if (!currentStudentProfile || !currentStudentProfile.student_id) {
            showToast('Anda harus masuk dulu.', 'error');
            return;
        }
        pendingQrToken = null;
        if (scanQrStatus) scanQrStatus.textContent = '';
        if (qrReader) qrReader.innerHTML = '';
        if (scanQrModal) scanQrModal.classList.remove('hidden');

        function startScanner() {
            if (typeof Html5Qrcode !== 'undefined') {
                if (html5QrCodeInstance) {
                    try { html5QrCodeInstance.stop(); } catch (e) {}
                    html5QrCodeInstance = null;
                }
                html5QrCodeInstance = new Html5Qrcode('qr-reader');
                var opts = { fps: 8, qrbox: { width: 220, height: 220 } };
                html5QrCodeInstance.start(
                    { facingMode: 'environment' },
                    opts,
                    function (decodedText) {
                        var token = (decodedText || '').trim();
                        if (!token) return;
                        try { html5QrCodeInstance.stop(); } catch (e) {}
                        html5QrCodeInstance = null;
                        pendingQrToken = token;
                        if (scanQrModal) scanQrModal.classList.add('hidden');
                        var titleEl = qrPinModal && qrPinModal.querySelector('.modal-panel-title');
                        if (titleEl) titleEl.textContent = 'Konfirmasi PIN';
                        var hintEl = qrPinModal && qrPinModal.querySelector('.modal-panel-hint');
                        if (hintEl) hintEl.textContent = 'Masukkan PIN untuk mengirim absen mapel.';
                        if (qrPinInput) { qrPinInput.value = ''; qrPinInput.focus(); }
                        if (qrPinModal) qrPinModal.classList.remove('hidden');
                    },
                    function () {}
                ).catch(function (err) {
                    if (scanQrStatus) scanQrStatus.textContent = 'Kamera tidak tersedia atau izin ditolak.';
                    showToast('Aktifkan izin kamera untuk scan QR.', 'error');
                });
            } else {
                if (scanQrStatus) scanQrStatus.textContent = 'Library scan QR tidak terbaca. Refresh halaman.';
            }
        }

        if (typeof Html5Qrcode !== 'undefined') {
            startScanner();
        } else {
            if (scanQrStatus) scanQrStatus.textContent = 'Memuat pemindai QR…';
            var s = document.createElement('script');
            s.src = 'https://unpkg.com/html5-qrcode@2.3.8/html5-qrcode.min.js';
            s.onload = startScanner;
            s.onerror = function () {
                if (scanQrStatus) scanQrStatus.textContent = 'Gagal memuat library. Cek koneksi.';
            };
            document.head.appendChild(s);
        }
    }

    function closeScanQrModal() {
        if (html5QrCodeInstance) {
            try { html5QrCodeInstance.stop(); } catch (e) {}
            html5QrCodeInstance = null;
        }
        if (scanQrModal) scanQrModal.classList.add('hidden');
    }

    function submitCheckinMasukPulang(pin) {
        if (!currentStudentProfile || !currentStudentProfile.nisn || !pendingMasukPulangType) return;
        var url = backendBase() + '/api/mobile/checkin-masuk-pulang';
        var payload = {
            nisn: currentStudentProfile.nisn,
            pin: pin,
            type: pendingMasukPulangType,
            lat: currentLat,
            lng: currentLng
        };
        if (qrPinSubmit) { qrPinSubmit.disabled = true; qrPinSubmit.textContent = 'Mengirim…'; }
        fetch(url, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },
            body: JSON.stringify(payload)
        })
            .then(function (r) { return r.json().then(function (d) { return { ok: r.ok, data: d }; }); })
            .then(function (r) {
                if (qrPinSubmit) { qrPinSubmit.disabled = false; qrPinSubmit.textContent = 'Kirim Absen'; }
                closeQrPinModal();
                pendingMasukPulangType = null;
                currentCheckinType = null;
                if (!r.ok) {
                    var msg = (r.data && r.data.message) ? r.data.message : 'Gagal mengirim absen';
                    showToast(msg, 'error');
                    setResult('ERROR', msg, '');
                    return;
                }
                var body = r.data || {};
                var status = (body.data && body.data.status) ? body.data.status : 'UNKNOWN';
                var message = body.message || status;
                var meta = (body.data && body.data.checkin_at) ? ('Waktu: ' + body.data.checkin_at) : '';
                var friendly = message;
                if (status === 'PRESENT') friendly = pendingMasukPulangType === 'masuk' ? 'Absen masuk berhasil.' : 'Absen pulang berhasil.';
                else if (status === 'OUTSIDE_ZONE') {
                    friendly = 'Anda di luar jangkauan sekolah.';
                    updateLocationStatus('Anda di luar jangkauan sekolah', true);
                }
                else if (status === 'INVALID_DEVICE') friendly = 'Device aplikasi belum dikonfigurasi. Hubungi admin.';
                else if (status === 'ALREADY_CHECKED_IN') friendly = pendingMasukPulangType === 'masuk' ? 'Sudah absen masuk hari ini.' : 'Sudah absen pulang hari ini.';
                else if (status === 'ABSENCE_WINDOW_CLOSED') friendly = 'Di luar jam absen. Cek jam masuk/pulang di pengaturan sekolah.';
                setResult(status, friendly, meta);
                showToast(friendly, (status === 'PRESENT') ? 'success' : 'info');
                fetchTodayStatus();
            })
            .catch(function () {
                if (qrPinSubmit) { qrPinSubmit.disabled = false; qrPinSubmit.textContent = 'Kirim Absen'; }
                pendingMasukPulangType = null;
                currentCheckinType = null;
                showToast('Tidak dapat terhubung ke server.', 'error');
                setResult('ERROR', 'Tidak dapat terhubung ke server.', '');
            });
    }

    function closeQrPinModal() {
        pendingQrToken = null;
        pendingMasukPulangType = null;
        if (qrPinModal) qrPinModal.classList.add('hidden');
        if (qrPinInput) qrPinInput.value = '';
        var titleEl = qrPinModal && qrPinModal.querySelector('.modal-panel-title');
        if (titleEl) titleEl.textContent = 'Konfirmasi PIN';
        var hintEl = qrPinModal && qrPinModal.querySelector('.modal-panel-hint');
        if (hintEl) hintEl.textContent = 'Masukkan PIN untuk mengirim absen mapel.';
    }

    function submitCheckinQr() {
        var pin = (qrPinInput && qrPinInput.value) ? qrPinInput.value.trim() : '';
        if (!pin) {
            showToast('Masukkan PIN.', 'error');
            return;
        }
        if (pendingMasukPulangType === 'masuk' || pendingMasukPulangType === 'pulang') {
            submitCheckinMasukPulang(pin);
            return;
        }
        if (!pendingQrToken || !currentStudentProfile || !currentStudentProfile.nisn) {
            showToast('Data tidak lengkap.', 'error');
            return;
        }
        var url = backendBase() + '/api/mobile/checkin-qr';
        var payload = {
            nisn: currentStudentProfile.nisn,
            pin: pin,
            qr_token: pendingQrToken
        };
        if (currentLat != null && currentLng != null) {
            payload.lat = currentLat;
            payload.lng = currentLng;
        }
        if (qrPinSubmit) { qrPinSubmit.disabled = true; qrPinSubmit.textContent = 'Mengirim…'; }
        fetch(url, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },
            body: JSON.stringify(payload)
        })
            .then(function (r) { return r.json().then(function (d) { return { ok: r.ok, data: d }; }); })
            .then(function (r) {
                if (qrPinSubmit) { qrPinSubmit.disabled = false; qrPinSubmit.textContent = 'Kirim Absen'; }
                closeQrPinModal();
                if (!r.ok) {
                    var msg = (r.data && r.data.message) ? r.data.message : 'Gagal mengirim absen';
                    showToast(msg, 'error');
                    setResult('ERROR', msg, '');
                    return;
                }
                var body = r.data || {};
                var status = (body.data && body.data.status) ? body.data.status : 'UNKNOWN';
                var message = body.message || status;
                var meta = (body.data && body.data.checkin_at) ? ('Waktu: ' + body.data.checkin_at) : '';
                var friendly = message;
                if (status === 'PRESENT') friendly = 'Absensi berhasil (Hadir).';
                else if (status === 'LATE') friendly = 'Absensi berhasil (Terlambat).';
                else if (status === 'OUTSIDE_ZONE') {
                    friendly = 'Anda di luar jangkauan yang diizinkan.';
                    updateLocationStatus('Anda di luar jangkauan yang diizinkan', true);
                }
                else if (status === 'INVALID_QR_TOKEN') friendly = 'QR tidak valid atau sudah kadaluarsa. Minta guru menampilkan QR lagi.';
                else if (status === 'STUDENT_NOT_IN_CLASS') friendly = 'Anda tidak termasuk kelas untuk mapel ini.';
                else if (status === 'ALREADY_CHECKED_IN') friendly = 'Sudah absen untuk mapel ini hari ini.';
                setResult(status, friendly, meta);
                showToast(friendly, (status === 'PRESENT' || status === 'LATE') ? 'success' : 'info');
            })
            .catch(function () {
                if (qrPinSubmit) { qrPinSubmit.disabled = false; qrPinSubmit.textContent = 'Kirim Absen'; }
                showToast('Tidak dapat terhubung ke server.', 'error');
                setResult('ERROR', 'Tidak dapat terhubung ke server.', '');
            });
    }

    // Event binding
    btnOpenSettings.addEventListener('click', openSettingsModal);
    btnCloseSettings.addEventListener('click', closeSettingsModal);
    btnSaveConfig.addEventListener('click', function () {
        saveConfig();
    });
    var btnTestConnection = document.getElementById('btn-test-connection');
    if (btnTestConnection) {
        btnTestConnection.addEventListener('click', function () {
            if (!backendUrlInput.value.trim()) {
                configStatus.textContent = 'Isi URL backend dulu.';
                configStatus.style.color = '#f97316';
                return;
            }
            testConnection();
        });
    }
    btnRefreshLocation.addEventListener('click', getLocation);
    if (btnAbsenMasuk) {
        btnAbsenMasuk.addEventListener('click', function () {
            currentCheckinType = 'masuk';
            doCheckin();
        });
    }
    if (btnAbsenPulang) {
        btnAbsenPulang.addEventListener('click', function () {
            currentCheckinType = 'pulang';
            doCheckin();
        });
    }
    var btnScanQr = document.getElementById('btn-scan-qr');
    if (btnScanQr) btnScanQr.addEventListener('click', openScanQrModal);
    btnCheckNisn.addEventListener('click', handleCheckNisn);
    btnCompleteRegister.addEventListener('click', handleCompleteRegister);

    if (cameraBtnCancel) {
        cameraBtnCancel.addEventListener('click', closeCameraModal);
    }
    if (cameraBtnCancel2) {
        cameraBtnCancel2.addEventListener('click', closeCameraModal);
    }
    var cameraBackdrop = document.getElementById('camera-backdrop');
    if (cameraBackdrop) {
        cameraBackdrop.addEventListener('click', closeCameraModal);
    }
    if (enrollFaceBackdrop) {
        enrollFaceBackdrop.addEventListener('click', function () {
            showToast('Rekam wajah wajib untuk melanjutkan.', 'info');
        });
    }
    if (enrollFaceStart) {
        enrollFaceStart.addEventListener('click', startEnrollCapture);
    }
    if (resultCloseBtn) {
        resultCloseBtn.addEventListener('click', function () {
            resultBox.classList.add('hidden');
        });
    }

    if (scanQrClose) scanQrClose.addEventListener('click', closeScanQrModal);
    var scanQrBackdrop = document.getElementById('scan-qr-backdrop');
    if (scanQrBackdrop) scanQrBackdrop.addEventListener('click', closeScanQrModal);
    if (qrPinClose) qrPinClose.addEventListener('click', closeQrPinModal);
    if (qrPinBackdrop) qrPinBackdrop.addEventListener('click', closeQrPinModal);
    if (qrPinCancel) qrPinCancel.addEventListener('click', closeQrPinModal);
    if (qrPinSubmit) qrPinSubmit.addEventListener('click', submitCheckinQr);

    var btnGoLogin = document.getElementById('btn-go-login');
    var btnGoRegister = document.getElementById('btn-go-register');
    var linkGoRegister = document.getElementById('link-go-register');

    if (btnGoLogin) {
        btnGoLogin.addEventListener('click', function () {
            goToScreen('login');
        });
    }
    if (btnGoRegister) {
        btnGoRegister.addEventListener('click', function () {
            goToScreen('register_nisn');
        });
    }
    if (linkGoRegister) {
        linkGoRegister.addEventListener('click', function () {
            goToScreen('register_nisn');
        });
    }

    var linkBackLogin1 = document.getElementById('link-back-login-1');
    if (linkBackLogin1) {
        linkBackLogin1.addEventListener('click', function (e) {
            e.preventDefault();
            goToScreen('login');
        });
    }

    var btnRegisterPinBatal = document.getElementById('btn-register-pin-batal');
    if (btnRegisterPinBatal) {
        btnRegisterPinBatal.addEventListener('click', function (e) {
            e.preventDefault();
            goToScreen('register_nisn');
        });
    }

    var btnLogout = document.getElementById('btn-logout');
    if (btnLogout) {
        btnLogout.addEventListener('click', function (e) {
            e.preventDefault();
            currentStudentProfile = null;
            try {
                localStorage.removeItem('sman1_student_profile');
            } catch (err) {}
            showToast('Anda sudah keluar', 'info');
            goToScreen('welcome');
        });
    }

    if (btnLogin) {
        btnLogin.addEventListener('click', handleLogin);
    }

    var linkForgotPin = document.getElementById('link-forgot-pin');
    if (linkForgotPin) {
        linkForgotPin.addEventListener('click', function (e) {
            e.preventDefault();
            goToScreen('forgot_pin');
        });
    }
    var linkBackToLogin = document.getElementById('link-back-to-login');
    if (linkBackToLogin) {
        linkBackToLogin.addEventListener('click', function (e) {
            e.preventDefault();
            goToScreen('login');
        });
    }
    if (btnResetPin) {
        btnResetPin.addEventListener('click', handleResetPin);
    }

    // Init
    loadConfig();
    currentStudentProfile = loadStudentProfile();
    if (currentStudentProfile && currentStudentProfile.student_id) {
        renderStudentSummary(homeStudentSummary, currentStudentProfile);
        goToScreen('home');
    } else {
        goToScreen('welcome');
    }
    getLocation();
})(); 

