"""
Simulation Runner — Core Engine
=================================
Chạy full loop: persona → chat → evaluate → report

Lấy từ MiroFish simulation_runner.py:
  ✅ Background simulation với progress tracking
  ✅ Retry/backoff khi gọi API
  ✅ Per-persona conversation logging

Model: gpt-5.4-nano
"""

import logging
from typing import Any

import httpx

from common.llm_factory import create_llm
from config import DEFAULT_MODEL

from .evaluator import evaluate_conversation
from .persona_generator import CanifaPersona, generate_personas

logger = logging.getLogger(__name__)

SIM_MODEL = DEFAULT_MODEL
DEFAULT_CHATBOT_URL = "http://172.16.2.207:5000/api/agent/chat-dev"


async def _generate_persona_message(
    persona: CanifaPersona,
    turn: int,
    conversation: list[dict[str, str]],
) -> str:
    """Persona AI tạo tin nhắn tiếp theo."""
    llm = create_llm(SIM_MODEL, streaming=False)

    if turn == 0:
        messages = [
            {"role": "system", "content": persona.system_prompt},
            {"role": "user", "content": (
                f"Bắt đầu chat với chatbot thời trang Canifa. "
                f"Gửi tin nhắn đầu tiên như {persona.name}. "
                f"1 tin nhắn ngắn, tự nhiên. KHÔNG giải thích."
            )},
        ]
    else:
        last_bot = conversation[-1]["content"] if conversation else ""
        messages = [
            {"role": "system", "content": persona.system_prompt},
            *conversation,
            {"role": "user", "content": (
                f'Chatbot vừa trả lời: "{last_bot[:500]}"\n\n'
                f"Tiếp tục chat như {persona.name}. 1 tin nhắn ngắn."
            )},
        ]

    response = await llm.ainvoke(messages)
    raw = response.content
    if isinstance(raw, list):
        raw = "".join(str(c.get("text", c) if isinstance(c, dict) else c) for c in raw)
    return raw.strip()


async def _send_to_chatbot(
    message: str,
    device_id: str,
    chatbot_url: str,
) -> dict[str, Any]:
    """Forward message to chatbot API with retry."""
    max_retries = 2

    for attempt in range(max_retries + 1):
        try:
            async with httpx.AsyncClient(timeout=60) as client:
                resp = await client.post(
                    chatbot_url,
                    json={"user_query": message, "device_id": device_id},
                    headers={"Content-Type": "application/json"},
                )
                resp.raise_for_status()
                data = resp.json()

            return {
                "reply": data.get("ai_response", ""),
                "product_ids": data.get("product_ids", []),
                "user_insight": data.get("user_insight"),
                "trace_id": data.get("trace_id", ""),
            }

        except Exception as e:
            if attempt < max_retries:
                logger.warning(f"⚠️ Chatbot retry {attempt+1}: {e}")
            else:
                logger.error(f"❌ Chatbot failed after {max_retries+1} attempts: {e}")
                return {
                    "reply": "[Chatbot không phản hồi]",
                    "product_ids": [],
                    "user_insight": None,
                    "trace_id": "",
                }


async def _simulate_one_persona(
    persona: CanifaPersona,
    turns: int,
    chatbot_url: str,
) -> dict[str, Any]:
    """
    Chạy simulation cho 1 persona:
    1. Generate message → 2. Send to bot → 3. Repeat N turns → 4. Evaluate
    """
    conversation: list[dict[str, str]] = []
    products_recommended: list = []
    device_id = f"sim-{persona.persona_id}"

    logger.info(f"💬 [{persona.name}] Starting {turns} turns...")

    for turn in range(turns):
        # 1. Persona generates message
        try:
            user_msg = await _generate_persona_message(persona, turn, conversation)
        except Exception as e:
            logger.error(f"❌ [{persona.name}] Message gen failed turn {turn}: {e}")
            break

        conversation.append({"role": "user", "content": user_msg})

        # 2. Send to chatbot
        bot_result = await _send_to_chatbot(user_msg, device_id, chatbot_url)
        bot_reply = bot_result["reply"]

        if bot_result["product_ids"]:
            products_recommended.extend(bot_result["product_ids"])

        conversation.append({"role": "assistant", "content": bot_reply})

        logger.info(
            f"  Turn {turn+1}: {persona.name}: {user_msg[:40]}... "
            f"→ Bot: {bot_reply[:40]}..."
        )

    # 3. Evaluate
    logger.info(f"📊 [{persona.name}] Evaluating...")
    eval_result = await evaluate_conversation(
        persona_name=persona.name,
        persona_age=persona.age,
        persona_archetype=persona.archetype,
        chat_style=persona.chat_style,
        budget=persona.budget,
        shopping_for=persona.shopping_for,
        style_preference=persona.style_preference,
        conversion_trigger=persona.conversion_trigger,
        drop_trigger=persona.drop_trigger,
        conversation=conversation,
    )

    scores = eval_result.get("scores", {})
    avg_score = sum(scores.values()) / max(len(scores), 1)

    logger.info(
        f"✅ [{persona.name}] {eval_result.get('conversion_status', '?')} | "
        f"Score: {avg_score:.1f}/5"
    )

    return {
        "persona_name": persona.name,
        "archetype": persona.archetype,
        "age": persona.age,
        "gender": persona.gender,
        "budget": persona.budget,
        "shopping_for": persona.shopping_for,
        "turns": len(conversation) // 2,
        "conversation": conversation,
        "products_recommended": products_recommended[:10],
        **eval_result,
    }


async def run_simulation(
    persona_count: int = 3,
    turns_per_persona: int = 5,
    archetypes: list[str] | None = None,
    chatbot_url: str | None = None,
    model_name: str | None = None,
) -> dict[str, Any]:
    """
    🔥 Run full automated simulation.

    Flow:
      1. Generate N personas (gpt-5.4-nano)
      2. Each persona chats with chatbot
      3. Evaluate each conversation
      4. Return conversion summary

    Args:
        persona_count: Number of personas (1-20)
        turns_per_persona: Chat turns per persona (1-10)
        archetypes: Optional customer archetypes
        chatbot_url: Chatbot API URL override
        model_name: LLM model override

    Returns:
        Full simulation report with conversion summary
    """
    url = chatbot_url or DEFAULT_CHATBOT_URL
    model = model_name or SIM_MODEL
    results: list[dict[str, Any]] = []
    errors: list[dict[str, str]] = []

    # ─── Step 1: Generate Personas ───
    logger.info(f"🎭 Step 1: Generating {persona_count} personas | model={model}")
    try:
        personas = await generate_personas(
            count=persona_count,
            archetypes=archetypes,
            model_name=model,
        )
    except Exception as e:
        logger.error(f"❌ Persona generation failed: {e}")
        return {"status": "error", "message": f"Persona generation failed: {e}"}

    logger.info(f"✅ Generated {len(personas)} personas")

    # ─── Step 2+3: Simulate each persona ───
    for persona in personas:
        try:
            result = await _simulate_one_persona(persona, turns_per_persona, url)
            results.append(result)
        except Exception as e:
            logger.error(f"❌ [{persona.name}] Simulation failed: {e}", exc_info=True)
            errors.append({"persona": persona.name, "error": str(e)})

    # ─── Step 4: Conversion Summary ───
    converted = sum(1 for r in results if r.get("conversion_status") == "converted")
    interested = sum(1 for r in results if r.get("conversion_status") == "interested")
    dropped = sum(1 for r in results if r.get("conversion_status") == "dropped")
    total = len(results)

    conversion_summary = {
        "total": total,
        "converted": converted,
        "interested": interested,
        "dropped": dropped,
        "conversion_rate": f"{converted / max(total, 1) * 100:.0f}%",
        "interest_rate": f"{(converted + interested) / max(total, 1) * 100:.0f}%",
    }

    # Average scores
    all_scores = [r.get("scores", {}) for r in results if r.get("scores")]
    if all_scores:
        avg_scores = {}
        for key in all_scores[0]:
            vals = [s.get(key, 0) for s in all_scores]
            avg_scores[key] = round(sum(vals) / len(vals), 1)
        conversion_summary["avg_scores"] = avg_scores

    logger.info(
        f"📊 SIMULATION COMPLETE: "
        f"{converted}/{total} converted ({conversion_summary['conversion_rate']}) | "
        f"Errors: {len(errors)}"
    )

    return {
        "status": "success",
        "conversion_summary": conversion_summary,
        "persona_results": results,
        "errors": errors,
        "meta": {
            "personas_generated": len(personas),
            "personas_tested": total,
            "turns_per_persona": turns_per_persona,
            "chatbot_url": url,
            "model": model,
        },
    }
