import os
import json
import asyncio
import logging
import pandas as pd
from dotenv import load_dotenv

# Ragas & Langchain
from ragas import evaluate, EvaluationDataset, RunConfig
from ragas.metrics import Faithfulness, AnswerRelevancy, ContextPrecision
from ragas.llms import LangchainLLMWrapper
from ragas.embeddings import LangchainEmbeddingsWrapper
from langchain_openai import ChatOpenAI
from langchain_community.embeddings import DashScopeEmbeddings

# Supabase Vector Retriever
import sys
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
from app.services.chatbot.vector_retriever import VectorRetriever
from app.services.chatbot.graph_retriever import GraphRetriever

logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

load_dotenv(override=True)

async def get_chatbot_contexts(active_hash: str) -> list[str]:
    """Mengambil konteks (chunks) menggunakan sistem Retriever yang sama dengan Chatbot."""
    graph_retriever = GraphRetriever()
    vector_retriever = VectorRetriever(table_name="document_chunks")
    
    await graph_retriever.connect()
    
    target_hashes = [active_hash]
    try:
        neighbors = await graph_retriever.get_relevant_neighbors(active_hash, max_nodes=2)
        if neighbors:
            target_hashes.extend([n["hash"] for n in neighbors if n.get("hash")])
    except Exception as e:
        pass # Abaikan jika Neo4j mati
    finally:
        await graph_retriever.close()
        
    raw_chunks = vector_retriever.fetch_chunks_by_hashes(target_hashes)
    return [chunk.get("content", "") for chunk in raw_chunks if chunk.get("content")]

async def main():
    print("="*50)
    print("[INFO] MEMULAI EVALUASI BATCHING (FULL QWEN)")
    print("[INFO] LLM Penjawab = Qwen, Juri = Qwen, Embeddings = Qwen")
    print("="*50)
    
    dashscope_api_key = os.getenv("DASHSCOPE_API_KEY")
    if not dashscope_api_key:
        logger.error("[ERROR] DASHSCOPE_API_KEY tidak ditemukan!")
        return

    # 1. Inisialisasi Qwen untuk Penjawab & Penilai
    qwen_llm = ChatOpenAI(
        api_key=dashscope_api_key,
        base_url="https://dashscope-intl.aliyuncs.com/compatible-mode/v1",
        model="qwen-plus",
        temperature=0.1
    )
    evaluator_llm = LangchainLLMWrapper(qwen_llm)
    
    # 2. Inisialisasi Qwen untuk Embeddings
    qwen_embeddings = DashScopeEmbeddings(
        dashscope_api_key=dashscope_api_key,
        model="text-embedding-v3"
    )
    evaluator_embeddings = LangchainEmbeddingsWrapper(qwen_embeddings)
    
    dataset_path = os.path.join(os.path.dirname(__file__), "..", "data", "eval_dataset.json")
    with open(dataset_path, "r") as f:
        raw_data = json.load(f)

    logger.info(f"Total pertanyaan: {len(raw_data)}")
    
    # Konfigurasi Batching: 5 prompt per batch
    batch_size = 5
    
    # Metrik Lengkap karena Embeddings Qwen sudah dipakai
    metrics = [
        Faithfulness(llm=evaluator_llm),
        AnswerRelevancy(llm=evaluator_llm, embeddings=evaluator_embeddings),
        ContextPrecision(llm=evaluator_llm)
    ]
    
    # Batasi worker di dalam Ragas agar tidak membludak
    config = RunConfig(timeout=180, max_workers=2)
    
    all_results_dfs = []

    for i in range(0, len(raw_data), batch_size):
        batch_num = (i // batch_size) + 1
        batch_data = raw_data[i:i+batch_size]
        
        print("\n" + "="*50)
        logger.info(f"[BATCH {batch_num}] Memproses Soal {i+1} sampai {i+len(batch_data)}")
        print("="*50)
        
        samples = []
        
        for item in batch_data:
            query = item.get("query")
            active_hash = item.get("active_hash")
            reference = item.get("ground_truth")
            
            logger.info(f"-> Simulasi Chatbot Qwen untuk: '{query}'")
            contexts = await get_chatbot_contexts(active_hash)
            
            if not contexts:
                logger.warning("Konteks kosong! Pastikan Hash valid di Supabase.")
                contexts = ["Tidak ada informasi di database."]
            
            # Simulasi Chatbot: Bypass FastAPI, langsung pakai Qwen untuk jawab
            context_str = "\n\n".join(contexts[:15]) # Batasi 15 chunk teratas
            prompt = (
                "Anda adalah asisten peneliti. Gunakan HANYA konteks berikut untuk menjawab pertanyaan.\n"
                "Jika informasi tidak ada di konteks, jawab 'Informasi tidak ditemukan di dokumen'.\n\n"
                f"Konteks:\n{context_str}\n\n"
                f"Pertanyaan: {query}"
            )
            
            try:
                response = qwen_llm.invoke(prompt)
                response_text = response.content
            except Exception as e:
                logger.error(f"Gagal generate jawaban (Cek Saldo/API Key Qwen): {e}")
                response_text = "Gagal memproses jawaban."
            
            samples.append({
                "user_input": query,
                "response": response_text,
                "retrieved_contexts": contexts[:15],
                "reference": reference
            })
            
        dataset_batch = EvaluationDataset.from_list(samples)
        
        logger.info(f"[BATCH {batch_num}] Menjalankan Penilaian Ragas...")
        try:
            batch_result = evaluate(
                dataset=dataset_batch,
                metrics=metrics,
                llm=evaluator_llm,
                embeddings=evaluator_embeddings,
                run_config=config
            )
            df_batch = batch_result.to_pandas()
            all_results_dfs.append(df_batch)
        except Exception as e:
            logger.error(f"[ERROR] Penilaian Ragas gagal di Batch {batch_num}: {e}")
            
        # Jeda 10 detik antar batch (kecuali batch terakhir)
        if i + batch_size < len(raw_data):
            logger.info(f"[JEDA BATCH] Menunggu 10 detik agar API Qwen bernapas...")
            await asyncio.sleep(10)
            
    if all_results_dfs:
        final_df = pd.concat(all_results_dfs, ignore_index=True)
        
        # Bulatkan nilai float menjadi 2 angka di belakang koma (misal: 0.6666667 -> 0.67)
        # agar lebih mudah dibaca dan tidak terlihat seperti "miliaran"
        final_df = final_df.round(2)
        
        print("\n" + "="*50)
        print("[RESULT] HASIL EVALUASI BATCHING (FULL QWEN)")
        print("="*50)
        print(final_df[['user_input', 'faithfulness', 'answer_relevancy', 'context_precision']].to_markdown())
        
        results_dir = os.path.join(os.path.dirname(__file__), "..", "results")
        os.makedirs(results_dir, exist_ok=True)
        out_path = os.path.join(results_dir, "evaluation_results_qwen_batch.csv")
        final_df.to_csv(out_path, index=False)
        logger.info(f"[SUCCESS] Laporan disimpan ke: {out_path}")

if __name__ == "__main__":
    if sys.platform == 'win32':
        asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
    asyncio.run(main())
