from flask import render_template, redirect, url_for, flash, request, g, abort
from flask_login import current_user
from datetime import date, datetime
from decimal import Decimal, InvalidOperation
import re
import os
import json
from app.guru import guru_bp
from app.auth.decorators import role_required, tenant_query, validate_tenant_resource
from app.models import (
    User, Kelas, KelasMurid, MataPelajaran, Jadwal, Nilai, Kehadiran,
    HukumBacaan, SetoranHafalan, SetoranHafalanAyat, NilaiImportBatch, NilaiImportRow
)
from app.extensions import db
from app.services.quran_service import get_chapters, get_verses


VALID_KEHADIRAN_STATUS = {'hadir', 'izin', 'sakit', 'alpha'}
VALID_NILAI_TYPES = {'tugas', 'uts', 'uas', 'kuis', 'praktik'}
IMPORT_COLUMN_ALIASES = {
    'murid': {'nama', 'nama murid', 'murid', 'siswa', 'santri', 'peserta didik', 'student'},
    'kelas': {'kelas', 'class', 'rombel'},
    'mapel': {'mapel', 'mata pelajaran', 'pelajaran', 'subject'},
    'surah': {'surah', 'surat', 'nama surah', 'nama surat'},
    'ayat': {'ayat', 'no ayat', 'nomor ayat', 'range ayat', 'ayat ke'},
    'score': {'nilai', 'skor', 'score', 'angka', 'hasil'},
    'type': {'jenis', 'jenis nilai', 'tipe', 'type', 'kategori'},
    'description': {'keterangan', 'deskripsi', 'catatan', 'komentar', 'materi'},
}


def _is_valid_hex_color(value):
    return bool(re.fullmatch(r'#[0-9a-fA-F]{6}', value or ''))


def _normalize_text(value):
    return re.sub(r'\s+', ' ', str(value or '').strip().lower())


def _normalize_key(value):
    return re.sub(r'[^a-z0-9]+', ' ', str(value or '').strip().lower()).strip()


def _canonical_column(header):
    normalized = _normalize_key(header)
    for canonical, aliases in IMPORT_COLUMN_ALIASES.items():
        if normalized in aliases:
            return canonical
    return None


def _read_excel_rows(file_storage):
    ext = os.path.splitext(file_storage.filename or '')[1].lower()
    if ext == '.xlsx':
        from openpyxl import load_workbook
        workbook = load_workbook(file_storage, read_only=True, data_only=True)
        sheet = workbook.active
        rows = list(sheet.iter_rows(values_only=True))
    elif ext == '.xls':
        import xlrd
        data = file_storage.read()
        workbook = xlrd.open_workbook(file_contents=data)
        sheet = workbook.sheet_by_index(0)
        rows = [sheet.row_values(i) for i in range(sheet.nrows)]
    else:
        raise ValueError('Format file harus .xlsx atau .xls')

    if not rows:
        return [], {}

    header_index = 0
    for idx, row in enumerate(rows[:10]):
        if sum(1 for value in row if str(value or '').strip()) >= 2:
            header_index = idx
            break

    headers = [str(value or '').strip() for value in rows[header_index]]
    column_map = {}
    for index, header in enumerate(headers):
        canonical = _canonical_column(header)
        if canonical and canonical not in column_map:
            column_map[canonical] = index

    parsed_rows = []
    for row_number, row in enumerate(rows[header_index + 1:], start=header_index + 2):
        raw = {}
        for index, header in enumerate(headers):
            if header:
                raw[header] = row[index] if index < len(row) else None
        if not any(str(value or '').strip() for value in raw.values()):
            continue
        parsed_rows.append((row_number, row, raw))

    return parsed_rows, column_map


def _cell(row, column_map, key):
    index = column_map.get(key)
    if index is None or index >= len(row):
        return None
    value = row[index]
    if isinstance(value, float) and value.is_integer():
        value = int(value)
    return str(value).strip() if value is not None else None


def _build_description(surah_name, ayat, description):
    parts = []
    if surah_name:
        parts.append(f'Surah: {surah_name}')
    if ayat:
        parts.append(f'Ayat: {ayat}')
    if description:
        parts.append(description)
    return ' | '.join(parts)[:255] if parts else None


def _get_surah_meta(surah_number):
    chapters = get_chapters(language='id')
    for chapter in chapters:
        if chapter.get('id') == surah_number:
            return {
                'name': chapter.get('translated_name', {}).get('name') or chapter.get('name_complex') or f'Surah {surah_number}',
                'verses_count': int(chapter.get('verses_count') or 0),
            }
    return None


def _load_verses_for_range(surah_number, start_verse, end_verse):
    verses_by_number = {}
    page = 1
    while len(verses_by_number) < (end_verse - start_verse + 1):
        data = get_verses(surah_number, page=page, per_page=15)
        verses = data.get('verses', [])
        if not verses:
            break
        for verse in verses:
            number = int(verse.get('verse_number') or 0)
            if start_verse <= number <= end_verse:
                verses_by_number[number] = verse
        pagination = data.get('pagination', {})
        if not pagination.get('next_page'):
            break
        page = int(pagination['next_page'])
    return verses_by_number


@guru_bp.route('/')
@role_required('guru')
def dashboard():
    jadwal_list = tenant_query(Jadwal).filter_by(guru_id=current_user.id).order_by(Jadwal.day, Jadwal.start_time).all()
    kelas_ids = list(set(j.kelas_id for j in jadwal_list))
    total_murid = KelasMurid.query.filter(
        KelasMurid.school_id == g.school_id,
        KelasMurid.kelas_id.in_(kelas_ids)
    ).count() if kelas_ids else 0

    # Show ALL kelas and mapel for the school
    total_kelas = tenant_query(Kelas).count()
    total_mapel = tenant_query(MataPelajaran).count()

    return render_template('guru/dashboard.html',
                           jadwal_list=jadwal_list,
                           total_murid=total_murid,
                           total_mapel=total_mapel,
                           total_kelas=total_kelas)


@guru_bp.route('/nilai', methods=['GET', 'POST'])
@role_required('guru')
def input_nilai():
    jadwal_list = tenant_query(Jadwal).filter_by(guru_id=current_user.id).all()
    kelas_ids_from_jadwal = set(j.kelas_id for j in jadwal_list)
    mapel_ids_from_jadwal = set(j.mata_pelajaran_id for j in jadwal_list)

    kelas_list = tenant_query(Kelas).filter(Kelas.id.in_(kelas_ids_from_jadwal)).order_by(Kelas.name).all() if kelas_ids_from_jadwal else []
    mapel_list = tenant_query(MataPelajaran).filter(MataPelajaran.id.in_(mapel_ids_from_jadwal)).order_by(MataPelajaran.name).all() if mapel_ids_from_jadwal else []

    selected_kelas = request.args.get('kelas_id', type=int)
    selected_mapel = request.args.get('mapel_id', type=int)
    murid_list = []
    existing_nilai = {}

    if selected_kelas and selected_mapel:
        kelas_valid = Kelas.query.get(selected_kelas)
        mapel_valid = MataPelajaran.query.get(selected_mapel)
        if (kelas_valid and mapel_valid and
                kelas_valid.school_id == g.school_id and
                mapel_valid.school_id == g.school_id and
                selected_kelas in kelas_ids_from_jadwal and
                selected_mapel in mapel_ids_from_jadwal):
            enrollments = KelasMurid.query.filter_by(school_id=g.school_id, kelas_id=selected_kelas).all()
            murid_list = [e.student for e in enrollments]
            for n in Nilai.query.filter_by(school_id=g.school_id, kelas_id=selected_kelas, mata_pelajaran_id=selected_mapel).all():
                existing_nilai[n.murid_id] = existing_nilai.get(n.murid_id, [])
                existing_nilai[n.murid_id].append(n)

    if request.method == 'POST':
        kelas_id = int(request.form['kelas_id'])
        mapel_id = int(request.form['mapel_id'])
        nilai_type = request.form['type']
        description = request.form.get('description', '')
        if nilai_type not in VALID_NILAI_TYPES:
            flash('Jenis nilai tidak valid.', 'error')
            return redirect(url_for('guru.input_nilai'))

        # Validate kelas and mapel belong to this school
        kelas = validate_tenant_resource(Kelas.query.get(kelas_id))
        mapel = validate_tenant_resource(MataPelajaran.query.get(mapel_id))
        if kelas_id not in kelas_ids_from_jadwal or mapel_id not in mapel_ids_from_jadwal:
            flash('Kelas atau mata pelajaran tidak valid.', 'error')
            return redirect(url_for('guru.input_nilai'))

        enrollments = KelasMurid.query.filter_by(school_id=g.school_id, kelas_id=kelas_id).all()
        count = 0
        for enrollment in enrollments:
            score = request.form.get(f'score_{enrollment.murid_id}')
            if score and score.strip():
                try:
                    score_value = Decimal(score)
                except InvalidOperation:
                    flash('Nilai harus berupa angka.', 'error')
                    return redirect(url_for('guru.input_nilai', kelas_id=kelas_id, mapel_id=mapel_id))
                if score_value < 0 or score_value > 100:
                    flash('Nilai harus berada di antara 0 dan 100.', 'error')
                    return redirect(url_for('guru.input_nilai', kelas_id=kelas_id, mapel_id=mapel_id))
                nilai = Nilai(
                    school_id=g.school_id,
                    murid_id=enrollment.murid_id,
                    mata_pelajaran_id=mapel_id,
                    kelas_id=kelas_id,
                    type=nilai_type,
                    score=score_value,
                    description=description
                )
                db.session.add(nilai)
                count += 1

        db.session.commit()
        flash(f'Berhasil menyimpan {count} nilai. Alhamdulillah!', 'success')
        return redirect(url_for('guru.input_nilai', kelas_id=kelas_id, mapel_id=mapel_id))

    return render_template('guru/input_nilai.html',
                           kelas_list=kelas_list, mapel_list=mapel_list,
                           murid_list=murid_list, existing_nilai=existing_nilai,
                           selected_kelas=selected_kelas, selected_mapel=selected_mapel)


@guru_bp.route('/nilai/import', methods=['GET', 'POST'])
@role_required('guru')
def nilai_import():
    jadwal_list = tenant_query(Jadwal).filter_by(guru_id=current_user.id).all()
    kelas_ids = {j.kelas_id for j in jadwal_list}
    mapel_ids = {j.mata_pelajaran_id for j in jadwal_list}
    kelas_list = tenant_query(Kelas).filter(Kelas.id.in_(kelas_ids)).order_by(Kelas.name).all() if kelas_ids else []
    mapel_list = tenant_query(MataPelajaran).filter(MataPelajaran.id.in_(mapel_ids)).order_by(MataPelajaran.name).all() if mapel_ids else []
    batches = NilaiImportBatch.query.filter_by(
        school_id=g.school_id,
        guru_id=current_user.id
    ).order_by(NilaiImportBatch.created_at.desc()).limit(20).all()

    if request.method == 'POST':
        upload = request.files.get('file')
        if not upload or not upload.filename:
            flash('Pilih file Excel terlebih dahulu.', 'error')
            return redirect(url_for('guru.nilai_import'))

        default_kelas_id = request.form.get('default_kelas_id', type=int)
        default_mapel_id = request.form.get('default_mapel_id', type=int)
        default_type = request.form.get('default_type', 'tugas')
        if default_type not in VALID_NILAI_TYPES:
            default_type = 'tugas'

        if default_kelas_id and default_kelas_id not in kelas_ids:
            abort(403)
        if default_mapel_id and default_mapel_id not in mapel_ids:
            abort(403)

        try:
            parsed_rows, column_map = _read_excel_rows(upload)
        except Exception as exc:
            flash(f'Gagal membaca Excel: {exc}', 'error')
            return redirect(url_for('guru.nilai_import'))

        if 'murid' not in column_map or 'score' not in column_map:
            flash('Excel minimal perlu kolom nama murid dan nilai/skor.', 'error')
            return redirect(url_for('guru.nilai_import'))

        murid_map = {
            _normalize_text(user.name): user
            for user in User.query.filter_by(school_id=g.school_id, role='murid').all()
        }
        kelas_map = {_normalize_text(kelas.name): kelas for kelas in kelas_list}
        mapel_map = {_normalize_text(mapel.name): mapel for mapel in mapel_list}

        batch = NilaiImportBatch(
            school_id=g.school_id,
            guru_id=current_user.id,
            filename=upload.filename[:255],
            default_kelas_id=default_kelas_id,
            default_mapel_id=default_mapel_id,
            default_type=default_type,
            total_rows=len(parsed_rows),
        )
        db.session.add(batch)
        db.session.flush()

        for row_number, row, raw in parsed_rows:
            result = _import_nilai_row(
                batch=batch,
                row_number=row_number,
                row=row,
                raw=raw,
                column_map=column_map,
                murid_map=murid_map,
                kelas_map=kelas_map,
                mapel_map=mapel_map,
                default_kelas_id=default_kelas_id,
                default_mapel_id=default_mapel_id,
                default_type=default_type,
            )
            if result.action == 'created':
                batch.created_count += 1
            elif result.action == 'updated':
                batch.updated_count += 1
            else:
                batch.skipped_count += 1

        db.session.commit()
        flash(f'Import selesai: {batch.created_count} baru, {batch.updated_count} update, {batch.skipped_count} dilewati.', 'success')
        return redirect(url_for('guru.nilai_import_detail', batch_id=batch.id))

    return render_template(
        'guru/nilai_import.html',
        kelas_list=kelas_list,
        mapel_list=mapel_list,
        batches=batches,
    )


def _import_nilai_row(batch, row_number, row, raw, column_map, murid_map, kelas_map, mapel_map,
                      default_kelas_id, default_mapel_id, default_type):
    murid_name = _cell(row, column_map, 'murid')
    kelas_name = _cell(row, column_map, 'kelas')
    mapel_name = _cell(row, column_map, 'mapel')
    surah_name = _cell(row, column_map, 'surah')
    ayat = _cell(row, column_map, 'ayat')
    type_value = _normalize_text(_cell(row, column_map, 'type')) or default_type
    description_value = _cell(row, column_map, 'description')
    score_raw = _cell(row, column_map, 'score')

    if type_value not in VALID_NILAI_TYPES:
        type_value = default_type

    description = _build_description(surah_name, ayat, description_value)
    row_record = NilaiImportRow(
        batch_id=batch.id,
        school_id=g.school_id,
        row_number=row_number,
        action='skipped',
        status='skipped',
        murid_name=murid_name,
        kelas_name=kelas_name,
        mapel_name=mapel_name,
        surah_name=surah_name,
        ayat=ayat,
        nilai_type=type_value,
        description=description,
        raw_data={key: str(value) if value is not None else None for key, value in raw.items()},
    )

    murid = murid_map.get(_normalize_text(murid_name))
    kelas = kelas_map.get(_normalize_text(kelas_name)) if kelas_name else None
    mapel = mapel_map.get(_normalize_text(mapel_name)) if mapel_name else None
    kelas_id = kelas.id if kelas else default_kelas_id
    mapel_id = mapel.id if mapel else default_mapel_id

    try:
        score = Decimal(str(score_raw).replace(',', '.'))
    except Exception:
        row_record.message = 'Nilai tidak valid.'
        db.session.add(row_record)
        return row_record

    row_record.score = score
    if not murid:
        row_record.message = 'Murid tidak ditemukan.'
    elif not kelas_id:
        row_record.message = 'Kelas tidak ditemukan dan default kelas tidak dipilih.'
    elif not mapel_id:
        row_record.message = 'Mata pelajaran tidak ditemukan dan default mapel tidak dipilih.'
    elif score < 0 or score > 100:
        row_record.message = 'Nilai harus 0-100.'
    else:
        existing = Nilai.query.filter_by(
            school_id=g.school_id,
            murid_id=murid.id,
            kelas_id=kelas_id,
            mata_pelajaran_id=mapel_id,
            type=type_value,
            description=description,
        ).first()
        if existing:
            row_record.action = 'updated'
            row_record.status = 'success'
            row_record.nilai_id = existing.id
            row_record.previous_score = existing.score
            row_record.previous_description = existing.description
            row_record.previous_type = existing.type
            existing.score = score
            existing.description = description
            existing.type = type_value
            row_record.message = 'Data lama diperbarui otomatis.'
        else:
            nilai = Nilai(
                school_id=g.school_id,
                murid_id=murid.id,
                kelas_id=kelas_id,
                mata_pelajaran_id=mapel_id,
                type=type_value,
                score=score,
                description=description,
            )
            db.session.add(nilai)
            db.session.flush()
            row_record.action = 'created'
            row_record.status = 'success'
            row_record.nilai_id = nilai.id
            row_record.message = 'Data baru dibuat.'

    db.session.add(row_record)
    return row_record


@guru_bp.route('/nilai/import/<int:batch_id>')
@role_required('guru')
def nilai_import_detail(batch_id):
    batch = validate_tenant_resource(NilaiImportBatch.query.get_or_404(batch_id))
    if batch.guru_id != current_user.id:
        abort(403)
    rows = batch.rows.order_by(NilaiImportRow.row_number).all()
    return render_template('guru/nilai_import_detail.html', batch=batch, rows=rows)


@guru_bp.route('/nilai/import/<int:batch_id>/rollback', methods=['POST'])
@role_required('guru')
def nilai_import_rollback(batch_id):
    batch = validate_tenant_resource(NilaiImportBatch.query.get_or_404(batch_id))
    if batch.guru_id != current_user.id:
        abort(403)
    if batch.is_rolled_back:
        flash('Batch import ini sudah pernah di-rollback.', 'warning')
        return redirect(url_for('guru.nilai_import_detail', batch_id=batch.id))

    for row in batch.rows.filter(NilaiImportRow.status == 'success').all():
        if row.action == 'created' and row.nilai:
            db.session.delete(row.nilai)
            row.rollback_applied = True
        elif row.action == 'updated' and row.nilai:
            row.nilai.score = row.previous_score
            row.nilai.description = row.previous_description
            row.nilai.type = row.previous_type
            row.rollback_applied = True

    batch.is_rolled_back = True
    batch.rolled_back_at = datetime.utcnow()
    db.session.commit()
    flash('Rollback import berhasil diterapkan.', 'success')
    return redirect(url_for('guru.nilai_import_detail', batch_id=batch.id))


@guru_bp.route('/kehadiran', methods=['GET', 'POST'])
@role_required('guru')
def input_kehadiran():
    jadwal_list = tenant_query(Jadwal).filter_by(guru_id=current_user.id).all()
    kelas_ids_from_jadwal = set(j.kelas_id for j in jadwal_list)

    kelas_list = tenant_query(Kelas).filter(Kelas.id.in_(kelas_ids_from_jadwal)).order_by(Kelas.name).all() if kelas_ids_from_jadwal else []

    selected_kelas = request.args.get('kelas_id', type=int)
    selected_date = request.args.get('date', date.today().isoformat())
    murid_list = []
    existing_kehadiran = {}

    if selected_kelas:
        kelas_valid = Kelas.query.get(selected_kelas)
        if kelas_valid and kelas_valid.school_id == g.school_id and selected_kelas in kelas_ids_from_jadwal:
            enrollments = KelasMurid.query.filter_by(school_id=g.school_id, kelas_id=selected_kelas).all()
            murid_list = [e.student for e in enrollments]
            for k in Kehadiran.query.filter_by(school_id=g.school_id, kelas_id=selected_kelas, date=selected_date).all():
                existing_kehadiran[k.murid_id] = k.status

    if request.method == 'POST':
        kelas_id = int(request.form['kelas_id'])
        tanggal = request.form['date']
        try:
            tanggal_value = date.fromisoformat(tanggal)
        except ValueError:
            flash('Tanggal tidak valid.', 'error')
            return redirect(url_for('guru.input_kehadiran'))

        # Validate kelas belongs to this school
        kelas = validate_tenant_resource(Kelas.query.get(kelas_id))
        if kelas_id not in kelas_ids_from_jadwal:
            flash('Kelas tidak valid.', 'error')
            return redirect(url_for('guru.input_kehadiran'))

        enrollments = KelasMurid.query.filter_by(school_id=g.school_id, kelas_id=kelas_id).all()
        count = 0
        for enrollment in enrollments:
            status = request.form.get(f'status_{enrollment.murid_id}')
            if status:
                if status not in VALID_KEHADIRAN_STATUS:
                    flash('Status kehadiran tidak valid.', 'error')
                    return redirect(url_for('guru.input_kehadiran', kelas_id=kelas_id, date=tanggal))
                existing = Kehadiran.query.filter_by(
                    school_id=g.school_id,
                    murid_id=enrollment.murid_id,
                    kelas_id=kelas_id,
                    date=tanggal_value
                ).first()
                if existing:
                    existing.status = status
                else:
                    kehadiran = Kehadiran(
                        school_id=g.school_id,
                        murid_id=enrollment.murid_id,
                        kelas_id=kelas_id,
                        date=tanggal_value,
                        status=status
                    )
                    db.session.add(kehadiran)
                count += 1

        db.session.commit()
        flash(f'Kehadiran {count} murid berhasil disimpan.', 'success')
        return redirect(url_for('guru.input_kehadiran', kelas_id=kelas_id, date=tanggal))

    return render_template('guru/input_kehadiran.html',
                           kelas_list=kelas_list, murid_list=murid_list,
                           existing_kehadiran=existing_kehadiran,
                           selected_kelas=selected_kelas, selected_date=selected_date)


@guru_bp.route('/setoran-hafalan')
@role_required('guru')
def setoran_hafalan_list():
    setoran_list = SetoranHafalan.query.filter_by(
        school_id=g.school_id,
        guru_id=current_user.id
    ).order_by(SetoranHafalan.created_at.desc()).all()
    return render_template('guru/setoran_hafalan_list.html', setoran_list=setoran_list)


@guru_bp.route('/setoran-hafalan/tambah', methods=['GET', 'POST'])
@role_required('guru')
def setoran_hafalan_add():
    murid_list = User.query.filter_by(school_id=g.school_id, role='murid', is_active=True).order_by(User.name).all()
    try:
        chapters = get_chapters(language='id')
    except Exception:
        chapters = []

    if request.method == 'POST':
        murid_id = request.form.get('murid_id', type=int)
        surah_number = request.form.get('surah_number', type=int)
        start_verse = request.form.get('start_verse', type=int)
        end_verse = request.form.get('end_verse', type=int)

        murid = User.query.filter_by(id=murid_id, school_id=g.school_id, role='murid', is_active=True).first()
        if not murid:
            flash('Murid tidak valid.', 'error')
            return redirect(url_for('guru.setoran_hafalan_add'))

        if not surah_number or not start_verse or not end_verse or start_verse < 1 or end_verse < start_verse:
            flash('Rentang ayat tidak valid.', 'error')
            return redirect(url_for('guru.setoran_hafalan_add'))

        if end_verse - start_verse + 1 > 20:
            flash('Maksimal 20 ayat per sesi setoran.', 'error')
            return redirect(url_for('guru.setoran_hafalan_add'))

        try:
            surah_meta = _get_surah_meta(surah_number)
        except Exception as exc:
            flash(f'Gagal memuat data surah: {exc}', 'error')
            return redirect(url_for('guru.setoran_hafalan_add'))

        if not surah_meta or end_verse > surah_meta['verses_count']:
            flash('Surah atau nomor ayat tidak valid.', 'error')
            return redirect(url_for('guru.setoran_hafalan_add'))

        try:
            verses_by_number = _load_verses_for_range(surah_number, start_verse, end_verse)
        except Exception:
            verses_by_number = {}

        setoran = SetoranHafalan(
            school_id=g.school_id,
            guru_id=current_user.id,
            murid_id=murid.id,
            surah_number=surah_number,
            surah_name=surah_meta['name'],
            start_verse=start_verse,
            end_verse=end_verse,
        )
        db.session.add(setoran)
        db.session.flush()

        for verse_number in range(start_verse, end_verse + 1):
            verse = verses_by_number.get(verse_number, {})
            item = SetoranHafalanAyat(
                setoran_id=setoran.id,
                school_id=g.school_id,
                verse_number=verse_number,
                verse_key=verse.get('verse_key') or f'{surah_number}:{verse_number}',
                verse_text=verse.get('text_uthmani') or verse.get('text_imlaei'),
            )
            db.session.add(item)

        db.session.commit()
        flash('Sesi setoran hafalan berhasil dibuat.', 'success')
        return redirect(url_for('guru.setoran_hafalan_detail', id=setoran.id))

    return render_template('guru/setoran_hafalan_form.html', murid_list=murid_list, chapters=chapters)


@guru_bp.route('/setoran-hafalan/<int:id>', methods=['GET', 'POST'])
@role_required('guru')
def setoran_hafalan_detail(id):
    setoran = validate_tenant_resource(SetoranHafalan.query.get_or_404(id))
    if setoran.guru_id != current_user.id:
        abort(403)

    items = setoran.items.order_by(SetoranHafalanAyat.verse_number).all()
    hukum_list = tenant_query(HukumBacaan).order_by(HukumBacaan.name).all()

    if request.method == 'POST':
        now = datetime.utcnow()
        valid_hukum_ids = {h.id for h in hukum_list}
        completed_count = 0

        for item in items:
            was_completed = item.is_completed
            item.is_completed = request.form.get(f'completed_{item.id}') == 'on'
            if item.is_completed and not was_completed:
                item.completed_at = now
            elif not item.is_completed:
                item.completed_at = None

            hukum_id = request.form.get(f'hukum_{item.id}', type=int)
            item.hukum_bacaan_id = hukum_id if hukum_id in valid_hukum_ids else None
            item.comment = request.form.get(f'comment_{item.id}', '').strip() or None

            if item.is_completed:
                completed_count += 1

        if completed_count >= 5 and setoran.five_ayat_completed_at is None:
            setoran.five_ayat_completed_at = now

        if completed_count == len(items):
            setoran.status = 'tuntas'
            if setoran.completed_at is None:
                setoran.completed_at = now
        else:
            setoran.status = 'berjalan'
            setoran.completed_at = None

        db.session.commit()
        flash('Setoran hafalan berhasil disimpan.', 'success')
        return redirect(url_for('guru.setoran_hafalan_detail', id=setoran.id))

    return render_template(
        'guru/setoran_hafalan_detail.html',
        setoran=setoran,
        items=items,
        hukum_list=hukum_list
    )


@guru_bp.route('/hukum-bacaan')
@role_required('guru')
def hukum_bacaan_list():
    list_hukum = tenant_query(HukumBacaan).order_by(HukumBacaan.name).all()
    return render_template('guru/hukum_bacaan_list.html', list_hukum=list_hukum)


@guru_bp.route('/hukum-bacaan/add', methods=['GET', 'POST'])
@role_required('guru')
def hukum_bacaan_add():
    if request.method == 'POST':
        name = request.form.get('name', '').strip()
        color = request.form.get('color', '#22c55e')
        if not _is_valid_hex_color(color):
            color = '#22c55e'
        
        if not name:
            flash('Nama hukum bacaan wajib diisi.', 'error')
            return redirect(url_for('guru.hukum_bacaan_add'))
            
        hukum = HukumBacaan(school_id=g.school_id, name=name, color=color)
        db.session.add(hukum)
        db.session.commit()
        flash(f'Hukum bacaan {name} berhasil ditambahkan.', 'success')
        return redirect(url_for('guru.hukum_bacaan_list'))
        
    return render_template('guru/hukum_bacaan_form.html')


@guru_bp.route('/hukum-bacaan/edit/<int:id>', methods=['GET', 'POST'])
@role_required('guru')
def hukum_bacaan_edit(id):
    hukum = validate_tenant_resource(HukumBacaan.query.get_or_404(id))
    if request.method == 'POST':
        hukum.name = request.form.get('name', '').strip()
        hukum.color = request.form.get('color', '#22c55e')
        if not _is_valid_hex_color(hukum.color):
            hukum.color = '#22c55e'
        if not hukum.name:
            flash('Nama hukum bacaan wajib diisi.', 'error')
            return render_template('guru/hukum_bacaan_form.html', hukum=hukum)
        db.session.commit()
        flash(f'Hukum bacaan {hukum.name} berhasil diperbarui.', 'success')
        return redirect(url_for('guru.hukum_bacaan_list'))
        
    return render_template('guru/hukum_bacaan_form.html', hukum=hukum)


@guru_bp.route('/hukum-bacaan/delete/<int:id>', methods=['POST'])
@role_required('guru')
def hukum_bacaan_delete(id):
    hukum = validate_tenant_resource(HukumBacaan.query.get_or_404(id))
    name = hukum.name
    db.session.delete(hukum)
    db.session.commit()
    flash(f'Hukum bacaan {name} berhasil dihapus.', 'info')
    return redirect(url_for('guru.hukum_bacaan_list'))
