"""Perhitungan progres belajar.

Tidak ada kolom persentase yang disimpan: materi kelas dan tanda "dipelajari"
dihitung ulang setiap kali dibutuhkan, supaya angka di layar selalu cocok dengan
preparat yang benar-benar ada hari ini.
"""

from __future__ import annotations

from flask import has_app_context


def _kueri(sql: str, params: tuple = (), satu: bool = False):
    """Kueri yang tetap jalan dari skrip/tes di luar request (buka app context)."""
    from app import app as flask_app, kueri
    if has_app_context():
        return kueri(sql, params, satu)
    with flask_app.app_context():
        return kueri(sql, params, satu)


def _dari(tabel: str, pengguna_id: int) -> list[int]:
    return [r["gambar_id"] for r in
            _kueri(f"SELECT gambar_id FROM {tabel} WHERE pengguna_id = %s", (pengguna_id,))]


def catat_kunjungan(pengguna_id: int, gambar_id: int) -> None:
    """Naikkan hitungan kunjungan (upsert: satu baris per pengguna+preparat)."""
    _kueri("INSERT INTO kunjungan (pengguna_id, gambar_id, terakhir, jumlah) "
           "VALUES (%s, %s, NOW(), 1) "
           "ON DUPLICATE KEY UPDATE terakhir = NOW(), jumlah = jumlah + 1",
           (pengguna_id, gambar_id))


def tandai(pengguna_id: int, gambar_id: int, nilai: bool = True) -> bool:
    """Tandai preparat sudah dipelajari (atau batalkan). Mengembalikan status baru."""
    if nilai:
        _kueri("INSERT IGNORE INTO belajar (pengguna_id, gambar_id) VALUES (%s, %s)",
               (pengguna_id, gambar_id))
    else:
        _kueri("DELETE FROM belajar WHERE pengguna_id = %s AND gambar_id = %s",
               (pengguna_id, gambar_id))
    return bool(nilai)


def sudah_dipelajari(pengguna_id: int, gambar_id: int) -> bool:
    return bool(_kueri("SELECT 1 AS a FROM belajar WHERE pengguna_id = %s AND gambar_id = %s",
                       (pengguna_id, gambar_id), satu=True))


def persen(dipelajari: int, total: int) -> int:
    if not total:
        return 0
    return round(100 * dipelajari / total)


def materi_kelas(kelas_id: int) -> list[int]:
    """Preparat aktif yang menjadi materi kelas: dibagikan atau milik dosen kelas."""
    baris = _kueri(
        "SELECT g.id FROM gambar g JOIN kelas k ON k.id = %s "
        "WHERE g.aktif = 1 AND (g.publik = 1 OR g.dosen_id = k.dosen_id) "
        "ORDER BY g.created_at, g.id", (kelas_id,))
    return [r["id"] for r in (baris or [])]


def ringkasan_mahasiswa(pengguna_id: int) -> list[dict]:
    """Satu baris per kelas yang diikuti: total materi, sudah dipelajari, persen."""
    kelas = _kueri("SELECT k.* FROM kelas k JOIN kelas_anggota ka ON ka.kelas_id = k.id "
                   "WHERE ka.pengguna_id = %s AND k.aktif = 1 ORDER BY k.nama", (pengguna_id,))
    dipelajari = set(_dari("belajar", pengguna_id))
    hasil = []
    for k in kelas or []:
        materi = materi_kelas(k["id"])
        sudah = [g for g in materi if g in dipelajari]
        hasil.append({
            "id": k["id"], "nama": k["nama"], "kode": k["kode_gabung"],
            "total": len(materi), "dipelajari": len(sudah),
            "persen": persen(len(sudah), len(materi)),
            "belum": [g for g in materi if g not in dipelajari],
            "deskripsi": k.get("deskripsi"),
        })
    return hasil


def rekap_kelas(kelas_id: int) -> list[dict]:
    """Rekap semua anggota kelas (untuk dosen/admin)."""
    total = len(materi_kelas(kelas_id))
    anggota = _kueri(
        "SELECT p.id, p.nama, p.pengguna, p.aktif, "
        "(SELECT COUNT(*) FROM belajar b WHERE b.pengguna_id = p.id AND b.gambar_id IN ("
        "  SELECT g.id FROM gambar g JOIN kelas k ON k.id = %s "
        "  WHERE g.aktif = 1 AND (g.publik = 1 OR g.dosen_id = k.dosen_id))) dipelajari, "
        "(SELECT MAX(terakhir) FROM kunjungan ku WHERE ku.pengguna_id = p.id) terakhir "
        "FROM pengguna p JOIN kelas_anggota ka ON ka.pengguna_id = p.id "
        "WHERE ka.kelas_id = %s ORDER BY p.nama", (kelas_id, kelas_id))
    hasil = []
    for a in anggota or []:
        a["total"] = total
        a["persen"] = persen(a["dipelajari"] or 0, total)
        hasil.append(a)
    return hasil
