"""Ujian dari sisi dosen: menyusun soal, meninjau jawaban, rekap nilai."""

from __future__ import annotations

import csv
import io
import json

from flask import (Blueprint, Response, abort, flash, render_template, request,
                   url_for, redirect)

from keamanan import butuh, pengguna_sekarang
import penilaian
from bp_ujian import daftar_soal, sapu_kedaluwarsa, tutup_pengerjaan

bp = Blueprint("dosen_ujian", __name__)


def _ujian(ujian_id: int) -> dict:
    from app import kueri
    ujian = kueri("SELECT u.*, k.nama kelas_nama FROM ujian u JOIN kelas k ON k.id = u.kelas_id "
                  "WHERE u.id = %s", (ujian_id,), satu=True)
    if not ujian:
        abort(404)
    saya = pengguna_sekarang()
    if saya["peran"] != "admin" and ujian["dosen_id"] != saya["id"]:
        abort(404)
    return ujian


@bp.post("/kelas/<int:kelas_id>/ujian")
@butuh("admin", "dosen")
def buat(kelas_id: int):
    from app import kueri
    saya = pengguna_sekarang()
    kelas = kueri("SELECT * FROM kelas WHERE id = %s", (kelas_id,), satu=True)
    if not kelas or (saya["peran"] != "admin" and kelas["dosen_id"] != saya["id"]):
        abort(404)
    judul = (request.form.get("judul") or "").strip() or "Ujian tanpa judul"
    ujian_id = kueri(
        "INSERT INTO ujian (kelas_id, dosen_id, judul, petunjuk, mulai, tutup, durasi_menit) "
        "VALUES (%s, %s, %s, %s, %s, %s, %s)",
        (kelas_id, kelas["dosen_id"], judul,
         (request.form.get("petunjuk") or "").strip() or None,
         request.form.get("mulai") or None, request.form.get("tutup") or None,
         int(request.form["durasi_menit"]) if request.form.get("durasi_menit") else None))
    flash("Ujian draf dibuat. Tambahkan soal, lalu terbitkan.", "ok")
    return redirect(url_for("dosen_ujian.editor", ujian_id=ujian_id))


@bp.get("/dosen/ujian/<int:ujian_id>")
@butuh("admin", "dosen")
def editor(ujian_id: int):
    from app import kueri
    ujian = _ujian(ujian_id)
    soal = kueri("SELECT s.*, g.judul FROM soal s JOIN gambar g ON g.id = s.gambar_id "
                 "WHERE s.ujian_id = %s ORDER BY s.urutan, s.id", (ujian_id,)) or []
    saya = pengguna_sekarang()
    preparat = kueri(
        "SELECT id, judul, file_gambar, file_proxy FROM gambar WHERE aktif = 1 "
        "AND (%s = 'admin' OR publik = 1 OR dosen_id = %s) ORDER BY judul",
        (saya["peran"], saya["id"]))
    return render_template("dosen_ujian.html", ujian=ujian, soal=soal, preparat=preparat)


@bp.post("/dosen/ujian/<int:ujian_id>")
@butuh("admin", "dosen")
def sunting(ujian_id: int):
    from app import kueri
    ujian = _ujian(ujian_id)
    aksi = request.form.get("aksi") or "meta"
    if aksi == "status":
        baru = request.form.get("status")
        if baru not in ("draf", "terbit", "tutup"):
            abort(400)
        if baru == "terbit" and not kueri("SELECT COUNT(*) c FROM soal WHERE ujian_id=%s",
                                          (ujian_id,), satu=True)["c"]:
            flash("Tambahkan minimal satu soal sebelum menerbitkan.", "galat")
        else:
            kueri("UPDATE ujian SET status=%s WHERE id=%s", (baru, ujian_id))
            flash(f"Status ujian: {baru}.", "ok")
    else:
        kueri("UPDATE ujian SET judul=%s, petunjuk=%s, mulai=%s, tutup=%s, durasi_menit=%s, "
              "acak=%s, tampilkan_kunci=%s WHERE id=%s",
              ((request.form.get("judul") or ujian["judul"]).strip(),
               (request.form.get("petunjuk") or "").strip() or None,
               request.form.get("mulai") or None, request.form.get("tutup") or None,
               int(request.form["durasi_menit"]) if request.form.get("durasi_menit") else None,
               1 if "1" in request.form.getlist("acak") else 0,
               1 if "1" in request.form.getlist("tampilkan_kunci") else 0,
               ujian_id))
        flash("Pengaturan ujian disimpan.", "ok")
    return redirect(url_for("dosen_ujian.editor", ujian_id=ujian_id))


@bp.post("/dosen/ujian/<int:ujian_id>/soal")
@butuh("admin", "dosen")
def tambah_soal(ujian_id: int):
    from app import kueri
    _ujian(ujian_id)
    try:
        gambar_id = int(request.form.get("gambar_id"))
        x = max(0.0, min(100.0, float(request.form.get("x_pos") or 50)))
        y = max(0.0, min(100.0, float(request.form.get("y_pos") or 50)))
    except (TypeError, ValueError):
        flash("Pilih preparat dan tandai titiknya dulu.", "galat")
        return redirect(url_for("dosen_ujian.editor", ujian_id=ujian_id))
    if not kueri("SELECT id FROM gambar WHERE id=%s AND aktif=1", (gambar_id,), satu=True):
        abort(404)
    tipe = "pilihan_ganda" if request.form.get("tipe") == "pilihan_ganda" else "isian_tunjuk"
    pertanyaan = (request.form.get("pertanyaan") or "").strip()
    if not pertanyaan:
        flash("Pertanyaan wajib diisi.", "galat")
        return redirect(url_for("dosen_ujian.editor", ujian_id=ujian_id))

    sinonim = [s.strip() for s in (request.form.get("sinonim") or "").split(",") if s.strip()]
    pilihan = [p.strip() for p in (request.form.get("pilihan") or "").split("|") if p.strip()]
    jawaban_pg = request.form.get("jawaban_pg")
    if tipe == "isian_tunjuk" and not (request.form.get("kunci_teks") or "").strip():
        flash("Kunci jawaban isian wajib diisi.", "galat")
        return redirect(url_for("dosen_ujian.editor", ujian_id=ujian_id))
    if tipe == "pilihan_ganda" and len(pilihan) < 2:
        flash("Pilihan ganda butuh minimal 2 opsi, dipisah tanda |.", "galat")
        return redirect(url_for("dosen_ujian.editor", ujian_id=ujian_id))

    urut = kueri("SELECT COALESCE(MAX(urutan),0)+1 n FROM soal WHERE ujian_id=%s",
                 (ujian_id,), satu=True)["n"]
    kueri("INSERT INTO soal (ujian_id, gambar_id, tipe, pertanyaan, x_pos, y_pos, kunci_teks, "
          "sinonim, pilihan, jawaban_pg, skor, pembahasan, urutan) "
          "VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)",
          (ujian_id, gambar_id, tipe, pertanyaan, round(x, 1), round(y, 1),
           (request.form.get("kunci_teks") or "").strip() or None,
           json.dumps(sinonim, ensure_ascii=False) if sinonim else None,
           json.dumps(pilihan, ensure_ascii=False) if pilihan else None,
           int(jawaban_pg) if jawaban_pg not in (None, "") else None,
           max(1, min(100, int(request.form.get("skor") or 10))),
           (request.form.get("pembahasan") or "").strip() or None, urut))
    flash("Soal ditambahkan.", "ok")
    return redirect(url_for("dosen_ujian.editor", ujian_id=ujian_id))


@bp.post("/dosen/ujian/<int:ujian_id>/soal/<int:soal_id>/hapus")
@butuh("admin", "dosen")
def hapus_soal(ujian_id: int, soal_id: int):
    from app import kueri
    _ujian(ujian_id)
    kueri("DELETE FROM soal WHERE id=%s AND ujian_id=%s", (soal_id, ujian_id))
    flash("Soal dihapus.", "ok")
    return redirect(url_for("dosen_ujian.editor", ujian_id=ujian_id))


@bp.get("/dosen/ujian/<int:ujian_id>/tinjau")
@butuh("admin", "dosen")
def tinjau(ujian_id: int):
    from app import kueri
    ujian = _ujian(ujian_id)
    sapu_kedaluwarsa()
    soal = kueri("SELECT * FROM soal WHERE ujian_id=%s ORDER BY urutan, id", (ujian_id,)) or []
    antre = {}
    for s in soal:
        antre[s["id"]] = kueri(
            "SELECT j.*, p.pengguna_id, peng.nama, p.id pengerjaan_id "
            "FROM jawaban j JOIN pengerjaan p ON p.id = j.pengerjaan_id "
            "JOIN pengguna peng ON peng.id = p.pengguna_id "
            "WHERE j.soal_id=%s AND j.benar IS NULL ORDER BY peng.nama", (s["id"],)) or []
    total_menunggu = sum(len(v) for v in antre.values())
    return render_template("tinjau.html", ujian=ujian, soal=soal, antre=antre,
                           total_menunggu=total_menunggu)


@bp.post("/dosen/ujian/<int:ujian_id>/tinjau/<int:jawaban_id>")
@butuh("admin", "dosen")
def nilai_satu(ujian_id: int, jawaban_id: int):
    from app import kueri
    _ujian(ujian_id)
    baris = kueri("SELECT j.* FROM jawaban j JOIN soal s ON s.id = j.soal_id "
                  "WHERE j.id=%s AND s.ujian_id=%s", (jawaban_id, ujian_id), satu=True)
    if not baris:
        abort(404)
    aksi = request.form.get("aksi") or "benar"
    if aksi == "semua":
        soal_id = baris["soal_id"]
        kueri("UPDATE jawaban SET benar=1, ditinjau_oleh=%s, ditinjau_pada=NOW() "
              "WHERE soal_id=%s AND benar IS NULL", (pengguna_sekarang()["id"], soal_id))
        flash("Semua jawaban pada soal itu diterima.", "ok")
    else:
        kueri("UPDATE jawaban SET benar=%s, ditinjau_oleh=%s, ditinjau_pada=NOW() WHERE id=%s",
              (1 if aksi == "benar" else 0, pengguna_sekarang()["id"], jawaban_id))
    penilaian.nilai_pengerjaan(baris["pengerjaan_id"])
    _perbarui_status(baris["pengerjaan_id"])
    return redirect(url_for("dosen_ujian.tinjau", ujian_id=ujian_id))


def _perbarui_status(pengerjaan_id: int) -> None:
    from app import kueri
    sisa = kueri("SELECT COUNT(*) c FROM jawaban WHERE pengerjaan_id=%s AND benar IS NULL",
                 (pengerjaan_id,), satu=True)["c"]
    kueri("UPDATE pengerjaan SET status=%s WHERE id=%s AND status <> 'jalan'",
          ("dinilai" if not sisa else "selesai", pengerjaan_id))


@bp.get("/dosen/ujian/<int:ujian_id>/rekap")
@butuh("admin", "dosen")
def rekap(ujian_id: int):
    from app import kueri
    ujian = _ujian(ujian_id)
    sapu_kedaluwarsa()
    baris = kueri(
        "SELECT p.*, peng.nama, peng.pengguna, "
        "(SELECT COUNT(*) FROM jawaban j WHERE j.pengerjaan_id=p.id AND j.benar IS NULL) menunggu "
        "FROM pengerjaan p JOIN pengguna peng ON peng.id = p.pengguna_id "
        "WHERE p.ujian_id=%s ORDER BY peng.nama", (ujian_id,)) or []
    for b in baris:
        penilaian.nilai_pengerjaan(b["id"])
        b["nilai"] = b["nilai"] or 0
    return render_template("rekap_ujian.html", ujian=ujian, baris=baris,
                           total_soal=len(daftar_soal(ujian, 0)))


@bp.get("/dosen/ujian/<int:ujian_id>/rekap.csv")
@butuh("admin", "dosen")
def rekap_csv(ujian_id: int):
    from app import kueri
    ujian = _ujian(ujian_id)
    sapu_kedaluwarsa()
    baris = kueri(
        "SELECT p.*, peng.nama, peng.pengguna, "
        "(SELECT COUNT(*) FROM jawaban j WHERE j.pengerjaan_id=p.id AND j.benar IS NULL) menunggu "
        "FROM pengerjaan p JOIN pengguna peng ON peng.id = p.pengguna_id "
        "WHERE p.ujian_id=%s ORDER BY peng.nama", (ujian_id,)) or []
    buf = io.StringIO()
    tulis = csv.writer(buf)
    tulis.writerow(["nama", "pengguna", "nilai", "status", "menunggu_tinjau", "selesai"])
    for b in baris:
        nilai = penilaian.nilai_pengerjaan(b["id"])["nilai"]
        tulis.writerow([b["nama"], b["pengguna"], nilai, b["status"], b["menunggu"],
                        b["selesai"].isoformat() if b["selesai"] else ""])
    return Response(buf.getvalue(), mimetype="text/csv",
                    headers={"Content-Disposition": f'attachment; filename="nilai-ujian-{ujian_id}.csv"'})
