"""
Prompt Optimizer Route
API endpoints cho Prompt Optimizer: test chatbot, judge responses, optimize prompts.

Flow:
1. run-test: Gọi chatbot API remote (CHATBOT_API_BASE from config.py) → lấy câu trả lời
2. judge: Codex (gpt-5.3-codex) đánh giá câu trả lời + verify bằng data thật
3. optimize: Gemini phân tích lỗi → sinh prompt mới
"""

import json
import logging
from typing import Any

import httpx
from fastapi import APIRouter
from fastapi.responses import JSONResponse
from pydantic import BaseModel

from common.llm_factory import create_llm
from config import CHATBOT_API_BASE, DEFAULT_MODEL

logger = logging.getLogger(__name__)
router = APIRouter(prefix="/api/prompt-optimizer", tags=["Prompt Optimizer"])

# ═══ Config ═══
CHATBOT_API_URL = f"{CHATBOT_API_BASE}/api/agent/chat-dev"
JUDGE_MODEL = DEFAULT_MODEL  # Gemini Flash-Lite
OPTIMIZER_MODEL = DEFAULT_MODEL


# ═══ Request/Response Models ═══
class RunTestRequest(BaseModel):
    prompt: str  # System prompt (for tracking only — bot uses Langfuse prompt)
    test_input: str  # User message to send


class JudgeRequest(BaseModel):
    test_input: str
    chatbot_output: str
    product_ids: list[str] = []
    expect_keywords: list[str] = []
    expect_question: bool = False


class OptimizeRequest(BaseModel):
    current_prompt: str
    test_results: list[dict[str, Any]]  # [{input, output, score, reasoning}]
    version: int = 1


# ═══ 1. RUN TEST — Call remote chatbot API ═══
@router.post("/run-test", summary="Run a test case through the remote chatbot")
async def run_test(req: RunTestRequest):
    """
    Call chatbot API tại CHATBOT_API_BASE/api/agent/chat-dev
    Lấy câu trả lời thật từ bot (dùng prompt Langfuse hiện tại).
    """
    try:
        async with httpx.AsyncClient(timeout=60) as client:
            resp = await client.post(
                CHATBOT_API_URL,
                json={"user_query": req.test_input},
                headers={"Content-Type": "application/json"},
            )
            resp.raise_for_status()
            data = resp.json()

        if data.get("status") != "success":
            raise ValueError(data.get("message", "Chatbot API error"))

        # Extract product IDs (may be list of dicts or strings)
        raw_pids = data.get("product_ids", [])
        product_ids = []
        for p in raw_pids:
            if isinstance(p, dict):
                product_ids.append(p.get("sku", p.get("product_code", "")))
            else:
                product_ids.append(str(p))

        return {
            "status": "success",
            "output": data.get("ai_response", ""),
            "product_ids": [p for p in product_ids if p][:10],
            "trace_id": data.get("trace_id", ""),
        }
    except httpx.HTTPStatusError as e:
        logger.error(f"Chatbot API HTTP error: {e}")
        return JSONResponse(status_code=502, content={"status": "error", "message": f"Chatbot API: {e.response.status_code}"})
    except Exception as e:
        logger.error(f"Run test error: {e}", exc_info=True)
        return JSONResponse(status_code=500, content={"status": "error", "message": str(e)})


# ═══ 2. JUDGE — AI Judge using Codex to evaluate chatbot response ═══
JUDGE_SYSTEM_PROMPT = """You are an expert AI Judge evaluating a fashion chatbot's response quality.

You will receive:
1. The customer's question
2. The chatbot's response
3. Product IDs the chatbot referenced (if any)

You have access to the search results and stock data provided below.

Evaluate the response on these 5 criteria (score 0.0 to 1.0 each):

1. **factual** — Is the information factually correct? Are product names, prices, availability accurate based on the tool data provided?
2. **relevance** — Does the response actually answer what the customer asked?
3. **tone** — Is the tone friendly, professional, and appropriate for a Vietnamese fashion consultant? (uses "dạ", "ạ", "bạn", "nhé")
4. **helpfulness** — Does the response provide actionable advice? Does it suggest alternatives, ask clarifying questions when needed?
5. **hallucination** — Does the response make up products/prices/promotions that don't exist? (1.0 = no hallucination, 0.0 = completely fabricated)

Respond in valid JSON only:
{
  "metrics": {
    "factual": 0.0,
    "relevance": 0.0,
    "tone": 0.0,
    "helpfulness": 0.0,
    "hallucination": 0.0
  },
  "total": 0.0,
  "reasoning": "Brief explanation of scoring in Vietnamese",
  "issues": ["issue1", "issue2"]
}"""


@router.post("/judge", summary="AI Judge evaluates chatbot response")
async def judge_response(req: JudgeRequest):
    """
    Judge evaluates the chatbot's response using Codex model.
    Independently verifies facts by calling product search/stock APIs.
    """
    try:
        # Step 1: Get real product data for verification
        verification_data = await _get_verification_data(req.test_input, req.product_ids)

        # Step 2: Build judge prompt with verification data
        judge_input = f"""## Customer Question
{req.test_input}

## Chatbot Response
{req.chatbot_output}

## Product IDs Referenced by Chatbot
{json.dumps(req.product_ids) if req.product_ids else "None"}

## Real Data from Database (for fact-checking)
{json.dumps(verification_data, ensure_ascii=False, indent=2)}

## Expected Keywords (test case config)
{json.dumps(req.expect_keywords)}

## Should Ask Clarifying Question?
{"Yes" if req.expect_question else "No"}

Now evaluate the chatbot's response based on the real data above."""

        # Step 3: Call Judge (Gemini Flash-Lite)
        judge_llm = create_llm(JUDGE_MODEL, streaming=False, json_mode=True)
        response = await judge_llm.ainvoke([
            {"role": "system", "content": JUDGE_SYSTEM_PROMPT},
            {"role": "user", "content": judge_input},
        ])

        raw = response.content
        if isinstance(raw, list):
            raw = "".join(str(c.get("text", c) if isinstance(c, dict) else c) for c in raw)
        result = json.loads(raw)

        # Ensure total is calculated
        if "metrics" in result:
            metrics = result["metrics"]
            result["total"] = round(sum(metrics.values()) / len(metrics), 3)

        return {"status": "success", **result}

    except Exception as e:
        logger.error(f"Judge error: {e}", exc_info=True)
        return JSONResponse(status_code=500, content={"status": "error", "message": str(e)})


# ═══ 3. OPTIMIZE — Rewrite prompt based on test results ═══
OPTIMIZER_SYSTEM_PROMPT = """You are an expert prompt engineer for a Vietnamese fashion e-commerce chatbot (Canifa).

Analyze the test results and rewrite the system prompt to improve chatbot performance.

Rules for improving prompts:
- Add specific instructions for weak areas (tone, factual accuracy, etc.)
- Include examples of good responses when helpful
- Keep Vietnamese tone instructions (thân thiện, chuyên nghiệp)
- Emphasize asking clarifying questions when customer intent is unclear
- Add rules to prevent hallucination (never fabricate products/prices)
- Keep the prompt concise but comprehensive

Respond ONLY in valid JSON:
{
  "analysis": "2-3 sentences analyzing what went wrong (in Vietnamese)",
  "changes": ["change1 in Vietnamese", "change2", "change3"],
  "new_prompt": "complete improved prompt in Vietnamese",
  "predicted_score": 0.0
}"""


@router.post("/optimize", summary="Optimizer rewrites the prompt")
async def optimize_prompt(req: OptimizeRequest):
    """
    Optimizer LLM analyzes test failures and rewrites the prompt.
    """
    try:
        # Build optimization context
        failing = [r for r in req.test_results if r.get("score", 1) < 0.7]
        passing = [r for r in req.test_results if r.get("score", 0) >= 0.7]
        avg_score = sum(r.get("score", 0) for r in req.test_results) / max(len(req.test_results), 1)

        opt_input = f"""## Current Prompt (v{req.version})
---
{req.current_prompt}
---

## Overall Score: {avg_score * 100:.1f}%

## Failing Test Cases ({len(failing)}):
{chr(10).join(f'''
### Test: "{r.get("input", "?")}"
- Output: "{str(r.get("output", ""))[:200]}"
- Score: {r.get("score", 0) * 100:.0f}%
- Issues: {json.dumps(r.get("issues", []), ensure_ascii=False)}
- Reasoning: {r.get("reasoning", "")}
''' for r in failing)}

## Passing Test Cases ({len(passing)}):
{chr(10).join(f'- "{r.get("input", "?")}" → {r.get("score", 0) * 100:.0f}%' for r in passing[:3])}

Rewrite the prompt to fix the failing cases while maintaining the passing ones."""

        optimizer_llm = create_llm(OPTIMIZER_MODEL, streaming=False, json_mode=True)
        response = await optimizer_llm.ainvoke([
            {"role": "system", "content": OPTIMIZER_SYSTEM_PROMPT},
            {"role": "user", "content": opt_input},
        ])

        raw = response.content
        if isinstance(raw, list):
            raw = "".join(str(c.get("text", c) if isinstance(c, dict) else c) for c in raw)
        result = json.loads(raw)
        return {"status": "success", **result}

    except Exception as e:
        logger.error(f"Optimize error: {e}", exc_info=True)
        return JSONResponse(status_code=500, content={"status": "error", "message": str(e)})


# ═══ HELPER: Get real data for verification ═══
async def _get_verification_data(query: str, product_ids: list[str]) -> dict:
    """
    Fetch real product/stock data from internal APIs for Judge verification.
    """
    import httpx

    data = {"search_results": [], "stock_info": {}}

    try:
        async with httpx.AsyncClient(timeout=10) as client:
            # Search for products matching the query
            search_resp = await client.get(
                f"{CHATBOT_API_BASE}/api/products/list",
                params={"search": query, "limit": 10, "sort": "quantity_sold", "order": "desc"},
            )
            if search_resp.status_code == 200:
                search_data = search_resp.json()
                if search_data.get("status") == "success":
                    data["search_results"] = [
                        {
                            "product_name": p.get("product_name"),
                            "sku": p.get("product_code"),
                            "sale_price": p.get("sale_price"),
                            "original_price": p.get("original_price"),
                            "discount_percent": p.get("discount_percent"),
                            "gender": p.get("gender_by_product"),
                            "is_new": p.get("is_new"),
                            "quantity_sold": p.get("quantity_sold"),
                        }
                        for p in search_data.get("products", [])[:5]
                    ]

            # Check stock for referenced product IDs
            for pid in product_ids[:3]:
                try:
                    stock_resp = await client.get(
                        f"{CHATBOT_API_BASE}/api/stock/check",
                        params={"product_code": pid},
                    )
                    if stock_resp.status_code == 200:
                        data["stock_info"][pid] = stock_resp.json()
                except Exception:
                    data["stock_info"][pid] = {"error": "could not check stock"}

    except Exception as e:
        logger.warning(f"Verification data fetch error: {e}")
        data["error"] = str(e)

    return data
