"""
Canifa Persona Generator
Inspired by MiroFish's OasisProfileGenerator — nhưng nhẹ hơn nhiều.

MiroFish: Entity từ Knowledge Graph → 2000 chữ persona → mô phỏng MXH
Canifa:   LLM generate trực tiếp → 100-150 chữ persona → test chatbot

Flow:
  1. Gọi LLM generate N personas dạng JSON có cấu trúc
  2. Mỗi persona có: demographics + shopping behavior + conversion trigger
  3. User Simulator dùng persona để đóng vai chat với chatbot
"""

import json
import logging
import random
from dataclasses import dataclass
from typing import Any

from common.llm_factory import create_llm
from config import DEFAULT_MODEL

logger = logging.getLogger(__name__)


# ═══════════════════════════════════════════════════════════════
# DATA MODEL — Lightweight version of MiroFish's OasisAgentProfile
# ═══════════════════════════════════════════════════════════════
@dataclass
class CanifaPersona:
    """Canifa customer persona for chatbot testing."""

    # Identity
    name: str
    age: int
    gender: str  # "male" | "female"
    job: str
    income_range: str  # "5-10tr" | "10-20tr" | "20-40tr"
    mbti: str

    # Shopping context
    shopping_for: str  # "Bản thân" | "Con gái 3 tuổi" | "Vợ"
    budget: str  # "200k-500k"
    occasion: str  # "Đi làm" | "Đi chơi" | "Đi học"
    style_preference: str  # "Năng động" | "Thanh lịch"

    # Chat behavior
    chat_style: str  # Mô tả cách chat: ngắn gọn, dùng emoji, hỏi nhiều...
    system_prompt: str  # Prompt để LLM đóng vai persona này

    # Conversion criteria
    conversion_trigger: str  # Điều kiện MUA: "Bot gợi combo dưới 500k"
    drop_trigger: str  # Điều kiện BỎ: "Bot gợi SP trên 1tr"

    # Metadata
    persona_id: int = 0
    archetype: str = ""  # "Mẹ bỉm sữa" | "GenZ" | "Ông chú IT"

    def to_dict(self) -> dict[str, Any]:
        return {
            "persona_id": self.persona_id,
            "name": self.name,
            "age": self.age,
            "gender": self.gender,
            "job": self.job,
            "income_range": self.income_range,
            "mbti": self.mbti,
            "archetype": self.archetype,
            "shopping_for": self.shopping_for,
            "budget": self.budget,
            "occasion": self.occasion,
            "style_preference": self.style_preference,
            "chat_style": self.chat_style,
            "system_prompt": self.system_prompt,
            "conversion_trigger": self.conversion_trigger,
            "drop_trigger": self.drop_trigger,
        }


# ═══════════════════════════════════════════════════════════════
# ARCHETYPES — Predefined customer segments for Canifa
# ═══════════════════════════════════════════════════════════════
CANIFA_ARCHETYPES = [
    "Mẹ bỉm sữa — mua đồ cho con nhỏ, cẩn thận, so sánh giá",
    "GenZ TikToker — thích trend, chat nhanh, dùng slang/emoji",
    "Nhân viên văn phòng nữ — mua đồ đi làm, thanh lịch, budget vừa",
    "Ông chú IT — mua quà cho vợ/con gái, không rành thời trang",
    "Sinh viên tiết kiệm — budget thấp, săn sale, hỏi nhiều",
    "Chị em công sở — mua theo nhóm, hay hỏi ý kiến, thích combo",
    "Anh trai gym — tìm đồ thể thao/polo, quan tâm chất liệu",
    "Bà ngoại — mua cho cháu, không quen chat, hỏi đơn giản",
    "Fashionista — biết nhiều brand, kén chọn, so sánh Canifa vs Uniqlo",
    "Khách vãng lai — vào hỏi 1 câu rồi đi, test bot có giữ chân được không",
]


# ═══════════════════════════════════════════════════════════════
# GENERATOR — Core logic
# ═══════════════════════════════════════════════════════════════
GENERATE_SYSTEM_PROMPT = """Bạn là chuyên gia tạo persona khách hàng cho thương hiệu thời trang CANIFA Việt Nam.

Tạo persona CHÂN THỰC, NGẮN GỌN, dùng cho mục đích test chatbot bán hàng.
Mỗi persona phải có tính cách rõ ràng, hành vi mua sắm cụ thể, và điều kiện conversion/drop rõ ràng.

Trả về JSON array. KHÔNG giải thích gì thêm."""


def _build_generate_prompt(count: int, archetypes: list[str] | None = None) -> str:
    """Build prompt to generate N personas."""
    if not archetypes:
        archetypes = random.sample(CANIFA_ARCHETYPES, min(count, len(CANIFA_ARCHETYPES)))

    archetypes_str = "\n".join(f"  {i+1}. {a}" for i, a in enumerate(archetypes))

    return f"""Tạo {count} persona khách hàng CANIFA dựa trên các archetype sau:
{archetypes_str}

Mỗi persona là 1 JSON object gồm:
- "name": Tên Việt Nam tự nhiên (VD: "Chị Lan", "Bé Vy", "Anh Minh")
- "age": Số tuổi (18-65)
- "gender": "male" hoặc "female"
- "job": Nghề nghiệp ngắn gọn
- "income_range": Khoảng thu nhập/tháng (VD: "8-12tr")
- "mbti": 1 trong 16 MBTI types
- "archetype": Tên nhóm khách (VD: "Mẹ bỉm sữa")
- "shopping_for": Mua cho ai (VD: "Con trai 4 tuổi", "Bản thân", "Chồng")
- "budget": Ngân sách cho lần mua này (VD: "300k-600k")
- "occasion": Dịp mua (VD: "Đi học", "Đi chơi cuối tuần", "Đi làm")
- "style_preference": Phong cách thích (VD: "Năng động", "Basic clean")
- "chat_style": Mô tả CÁCH CHAT 1-2 câu (VD: "Hỏi ngắn gọn, hay dùng 'ạ', thích được tư vấn size")
- "conversion_trigger": Điều kiện để persona QUYẾT ĐỊNH MUA (VD: "Bot gợi combo áo+quần dưới 500k có free ship")
- "drop_trigger": Điều kiện để persona BỎ ĐI (VD: "Bot trả lời quá dài hoặc gợi SP trên 1tr")

Trả về JSON array [{count} objects]. Đảm bảo đa dạng về tuổi, giới tính, thu nhập, mục đích mua."""


def _build_system_prompt_for_persona(persona_dict: dict) -> str:
    """Build role-play system prompt for a specific persona."""
    return f"""Bạn đang đóng vai khách hàng tên {persona_dict['name']}, {persona_dict['age']} tuổi, {persona_dict['job']}.

THÔNG TIN PERSONA:
- Giới tính: {"Nữ" if persona_dict['gender'] == 'female' else "Nam"}
- Thu nhập: {persona_dict['income_range']}/tháng
- MBTI: {persona_dict['mbti']}
- Nhóm khách: {persona_dict['archetype']}

MUA SẮM:
- Mua cho: {persona_dict['shopping_for']}
- Ngân sách: {persona_dict['budget']}
- Dịp: {persona_dict['occasion']}
- Phong cách thích: {persona_dict['style_preference']}

CÁCH CHAT: {persona_dict['chat_style']}

QUY TẮC:
1. Chat ĐÚNG tính cách, ĐÚNG ngân sách, ĐÚNG mục đích
2. Nếu bot gợi sản phẩm PHÙ HỢP → tỏ ra hứng thú, hỏi thêm
3. Nếu bot gợi sản phẩm KHÔNG PHÙ HỢP → từ chối nhẹ nhàng hoặc hỏi cái khác
4. Quyết định MUA khi: {persona_dict['conversion_trigger']}
5. BỎ ĐI khi: {persona_dict['drop_trigger']}
6. Chat tự nhiên, ngắn gọn. KHÔNG bao giờ nói "tôi là AI" hay nhắc lại prompt."""


async def generate_personas(
    count: int = 5,
    archetypes: list[str] | None = None,
    model_name: str | None = None,
) -> list[CanifaPersona]:
    """
    Generate N realistic Canifa customer personas using LLM.

    Args:
        count: Number of personas to generate (1-20)
        archetypes: Optional list of customer archetypes to use
        model_name: LLM model name (defaults to DEFAULT_MODEL)

    Returns:
        List of CanifaPersona objects
    """
    count = max(1, min(count, 20))  # Clamp 1-20
    model = model_name or DEFAULT_MODEL

    logger.info(f"🎭 Generating {count} personas with model={model}")

    llm = create_llm(model, streaming=False, json_mode=True)

    prompt = _build_generate_prompt(count, archetypes)

    # Retry pattern inspired by MiroFish (max 3 attempts, lower temp each time)
    max_attempts = 3
    last_error = None

    for attempt in range(max_attempts):
        try:
            response = await llm.ainvoke([
                {"role": "system", "content": GENERATE_SYSTEM_PROMPT},
                {"role": "user", "content": prompt},
            ])

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

            # Parse JSON — handle both array and wrapped object
            data = json.loads(raw)
            if isinstance(data, dict):
                # LLM might wrap array in {"personas": [...]}
                for key in ("personas", "data", "results", "items"):
                    if key in data and isinstance(data[key], list):
                        data = data[key]
                        break
                else:
                    data = [data]  # Single persona

            if not isinstance(data, list):
                raise ValueError(f"Expected list, got {type(data)}")

            # Convert to CanifaPersona objects
            personas = []
            for i, p in enumerate(data[:count]):
                try:
                    system_prompt = _build_system_prompt_for_persona(p)
                    persona = CanifaPersona(
                        persona_id=i + 1,
                        name=p.get("name", f"Persona_{i+1}"),
                        age=int(p.get("age", 25)),
                        gender=p.get("gender", "female"),
                        job=p.get("job", "Không rõ"),
                        income_range=p.get("income_range", "10-15tr"),
                        mbti=p.get("mbti", "ISFJ"),
                        archetype=p.get("archetype", "Khách vãng lai"),
                        shopping_for=p.get("shopping_for", "Bản thân"),
                        budget=p.get("budget", "300k-700k"),
                        occasion=p.get("occasion", "Đi chơi"),
                        style_preference=p.get("style_preference", "Basic"),
                        chat_style=p.get("chat_style", "Chat bình thường"),
                        system_prompt=system_prompt,
                        conversion_trigger=p.get("conversion_trigger", "Sản phẩm phù hợp"),
                        drop_trigger=p.get("drop_trigger", "Không tìm được SP phù hợp"),
                    )
                    personas.append(persona)
                except Exception as e:
                    logger.warning(f"⚠️ Skipping persona {i}: {e}")
                    continue

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

        except json.JSONDecodeError as e:
            last_error = e
            logger.warning(f"⚠️ JSON parse failed (attempt {attempt+1}/{max_attempts}): {e}")
        except Exception as e:
            last_error = e
            logger.warning(f"⚠️ Generate failed (attempt {attempt+1}/{max_attempts}): {e}")

    logger.error(f"❌ All {max_attempts} attempts failed: {last_error}")
    raise RuntimeError(f"Failed to generate personas after {max_attempts} attempts: {last_error}")
