"""
berita_acara_service.py — Service layer untuk pemrosesan Berita Acara Sidang via GPT.

Alur:
1. Ambil teks transkripsi dari task (dekripsi Fernet)
2. Ambil prompt aktif dari DB (atau default hardcoded)
3. Panggil OpenAI GPT API → terima JSON array Q&A
4. Enkripsi hasilnya → simpan ke DB

Format output JSON dari GPT:
[
  {"id": 1, "question": "Nama terdakwa?", "answer": "Budi Santoso"},
  {"id": 2, "question": "Pasal yang didakwakan?", "answer": "Pasal 372 KUHP"},
  ...
]
"""

import json
import logging
import time
from typing import Optional

from sqlalchemy.orm import Session

from app.models.berita_acara import BeritaAcara, BeritaAcaraPrompt
from app.models.transkripsi import TranskripsiTask
from app.utils.crypto_utils import decrypt_text, encrypt_text
from app.core.config import OPENAI_API_KEY

logger = logging.getLogger(__name__)

# ── Default prompt (dipakai jika DB kosong) ──────────────────────────
DEFAULT_PROMPT = """Kamu adalah asisten hukum profesional yang membantu menyusun Berita Acara Sidang pengadilan Indonesia.

Berdasarkan transkripsi sidang di bawah ini, ekstrak semua pasangan tanya-jawab yang terjadi selama persidangan. 
Setiap pertanyaan yang diajukan (oleh hakim, jaksa, atau pengacara) beserta jawaban dari pihak yang ditanya harus diidentifikasi.

INSTRUKSI:
- Identifikasi setiap pertanyaan dan jawaban yang jelas
- Bersihkan teks dari disfluensi (eee, mmm, dan sejenisnya)
- Gunakan bahasa Indonesia formal sesuai standar dokumen hukum
- Nomori setiap pasangan Q&A secara berurutan mulai dari 1
- Jika satu pertanyaan dijawab panjang, tetap jadikan satu pasangan
- Jika ada beberapa pertanyaan berurutan tanpa jawaban, kelompokkan sebagai satu entri

FORMAT OUTPUT (JSON saja, tanpa teks lain, tanpa markdown):
[
  {"id": 1, "question": "Pertanyaan pertama?", "answer": "Jawaban pertama."},
  {"id": 2, "question": "Pertanyaan kedua?", "answer": "Jawaban kedua."}
]

TRANSKRIPSI:
{transcript}"""


def get_active_prompt(db: Session) -> str:
    """Ambil prompt aktif dari DB. Fallback ke DEFAULT_PROMPT jika tidak ada."""
    prompt_row = (
        db.query(BeritaAcaraPrompt)
        .filter(BeritaAcaraPrompt.is_active == True)
        .order_by(BeritaAcaraPrompt.updated_at.desc())
        .first()
    )
    if prompt_row and prompt_row.prompt_text:
        return prompt_row.prompt_text
    return DEFAULT_PROMPT


def upsert_prompt(db: Session, prompt_text: str, admin_id: int) -> BeritaAcaraPrompt:
    """Simpan atau update prompt di DB."""
    existing = db.query(BeritaAcaraPrompt).filter(
        BeritaAcaraPrompt.name == "default"
    ).first()

    if existing:
        existing.prompt_text = prompt_text
        existing.is_active   = True
        existing.updated_by  = admin_id
        db.commit()
        db.refresh(existing)
        return existing
    else:
        new_prompt = BeritaAcaraPrompt(
            name        = "default",
            prompt_text = prompt_text,
            is_active   = True,
            updated_by  = admin_id,
        )
        db.add(new_prompt)
        db.commit()
        db.refresh(new_prompt)
        return new_prompt


def get_transcript_text(task: TranskripsiTask) -> Optional[str]:
    """Dekripsi hasil transkripsi dari task."""
    if not task.result:
        return None
    return decrypt_text(task.result)


def process_berita_acara(ba_id: int) -> None:
    """
    Proses BeritaAcara di thread pool:
    1. Ambil teks transkripsi
    2. Kirim ke GPT API
    3. Parse JSON response
    4. Enkripsi & simpan ke DB
    """
    from app.core.database import get_db_context

    with get_db_context() as db:
        ba: BeritaAcara = db.query(BeritaAcara).filter(BeritaAcara.id == ba_id).first()
        if not ba:
            logger.warning("[BA] BeritaAcara ID %d tidak ditemukan.", ba_id)
            return

        task = db.query(TranskripsiTask).filter(TranskripsiTask.id == ba.task_id).first()
        if not task or task.status != "completed":
            _set_ba_failed(db, ba, "Task transkripsi belum selesai atau tidak ditemukan.")
            return

        transcript = get_transcript_text(task)
        if not transcript or not transcript.strip():
            _set_ba_failed(db, ba, "Teks transkripsi kosong, tidak bisa diproses.")
            return

        prompt_template = ba.prompt_used or get_active_prompt(db)
        ba.status = "processing"
        db.commit()

    # ── Panggil GPT di luar sesi DB ─────────────────────────────
    try:
        qa_list, model_used, elapsed = _call_gpt(transcript, prompt_template)
    except Exception as exc:
        logger.error("[BA] GPT error untuk BA ID %d: %s", ba_id, exc, exc_info=True)
        with get_db_context() as db:
            ba = db.query(BeritaAcara).filter(BeritaAcara.id == ba_id).first()
            if ba:
                _set_ba_failed(db, ba, f"GPT API error: {str(exc)[:300]}")
        return

    # ── Enkripsi & simpan ────────────────────────────────────────
    with get_db_context() as db:
        ba = db.query(BeritaAcara).filter(BeritaAcara.id == ba_id).first()
        if not ba:
            return

        encrypted = encrypt_text(json.dumps(qa_list, ensure_ascii=False))
        ba.qa_json_encrypted = encrypted
        ba.status            = "completed"
        ba.model_used        = model_used
        ba.processing_time   = str(round(elapsed, 2))
        ba.error_message     = None
        db.commit()

    logger.info("[BA] BeritaAcara ID %d selesai (%d Q&A, %.2fs).", ba_id, len(qa_list), elapsed)


def _call_gpt(transcript: str, prompt_template: str) -> tuple[list, str, float]:
    """
    Panggil OpenAI ChatCompletion API.
    Returns: (qa_list, model_name, elapsed_seconds)
    """
    from openai import OpenAI

    api_key = OPENAI_API_KEY
    if not api_key:
        raise ValueError("OPENAI_API_KEY belum diset di environment.")

    client = OpenAI(api_key=api_key)
    model  = "gpt-4o-mini"

    # Batasi panjang transkripsi (~80k char ≈ 20k token)
    transcript_trimmed = transcript[:80000]

    final_prompt = prompt_template.replace("{transcript}", transcript_trimmed)

    start = time.perf_counter()
    response = client.chat.completions.create(
        model       = model,
        messages    = [
            {
                "role":    "system",
                "content": "Kamu adalah asisten hukum. Selalu balas HANYA dengan JSON array valid, tanpa teks tambahan apapun.",
            },
            {
                "role":    "user",
                "content": final_prompt,
            },
        ],
        temperature = 0.2,
        max_tokens  = 4096,
    )
    elapsed = time.perf_counter() - start

    raw_content = response.choices[0].message.content or ""

    # Bersihkan markdown code fences jika ada
    raw_content = raw_content.strip()
    if raw_content.startswith("```"):
        raw_content = raw_content.split("```")[1]
        if raw_content.startswith("json"):
            raw_content = raw_content[4:]
        raw_content = raw_content.rstrip("`").strip()

    qa_list = json.loads(raw_content)

    if not isinstance(qa_list, list):
        raise ValueError("GPT tidak mengembalikan array JSON.")

    # Validasi & normalisasi setiap item
    cleaned = []
    for i, item in enumerate(qa_list, start=1):
        if not isinstance(item, dict):
            continue
        cleaned.append({
            "id":       item.get("id", i),
            "question": str(item.get("question", "")).strip(),
            "answer":   str(item.get("answer", "")).strip(),
        })

    return cleaned, model, elapsed


def _set_ba_failed(db: Session, ba: BeritaAcara, msg: str) -> None:
    ba.status        = "failed"
    ba.error_message = msg
    db.commit()


def get_ba_qa_decrypted(ba: BeritaAcara) -> list:
    """Dekripsi dan parse qa_json dari BeritaAcara. Return [] jika kosong/error."""
    if not ba.qa_json_encrypted:
        return []
    try:
        raw = decrypt_text(ba.qa_json_encrypted)
        parsed = json.loads(raw)
        return parsed if isinstance(parsed, list) else []
    except Exception:
        return []


def save_qa_edits(db: Session, ba: BeritaAcara, qa_list: list) -> BeritaAcara:
    """Simpan hasil edit Q&A dari frontend (re-encrypt)."""
    encrypted = encrypt_text(json.dumps(qa_list, ensure_ascii=False))
    ba.qa_json_encrypted = encrypted
    db.commit()
    db.refresh(ba)
    return ba