from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import AllowAny
from rest_framework.response import Response
from rest_framework import status

from apps.agents.models import AgentConfig, AgentVoiceConfig, AgentGuardrail, AgentSystemTool
from django.db import connections
import json
from apps.cluster.dynamic_tables import _q_ident, ensure_business_cluster_tables


@api_view(["GET"])
@permission_classes([AllowAny])
def agent_list(request):
    """List available agent names (for discovery)."""
    names = list(AgentConfig.objects.values_list("name", flat=True))
    return Response({"agents": names})


@api_view(["GET"])
@permission_classes([AllowAny])
def agent_config(request, agent_name):
    """Worker fetches full config for an agent by name."""
    try:
        agent = AgentConfig.objects.prefetch_related(
            "voice_config", "guardrails", "system_tools", "knowledge_base"
        ).get(name=agent_name)
    except AgentConfig.DoesNotExist:
        return Response({"error": "Agent not found"}, status=status.HTTP_404_NOT_FOUND)

    voice = getattr(agent, "voice_config", None)
    data = {
        "id": agent.pk,
        "name": agent.name,
        "mcube_exenumber": agent.mcube_exenumber,
        "mcube_gid": agent.mcube_gid,
        "stt_provider": agent.stt_provider,
        "tts_provider": agent.tts_provider,
        "llm_provider": agent.llm_provider,
        "llm_model": agent.llm_model,
        "system_prompt": agent.system_prompt,
        "default_language": agent.default_language,
        "first_message_inbound": agent.first_message_inbound,
        "first_message_outbound": agent.first_message_outbound,
        "knowledge_base_id": str(agent.knowledge_base_id) if agent.knowledge_base_id else None,
        "voice": {
            "voice_id": voice.voice_id if voice else None,
            "tts_model_family": voice.tts_model_family if voice else None,
            "similarity": voice.similarity if voice else None,
            "speed": voice.speed if voice else None,
            "stability": voice.stability if voice else None,
            "output_format": voice.output_format if voice else None,
        } if voice else {},
        "guardrails": [g.guardrail_key for g in agent.guardrails.filter(enabled=True)],
        "system_tools": [t.tool_key for t in agent.system_tools.filter(enabled=True)],
    }
    return Response(data)


def _json_dict(val) -> dict:
    if val is None:
        return {}
    if isinstance(val, dict):
        return val
    if isinstance(val, (bytes, bytearray)):
        val = val.decode("utf-8", errors="replace")
    if isinstance(val, str) and val.strip():
        try:
            parsed = json.loads(val)
            return parsed if isinstance(parsed, dict) else {}
        except Exception:
            return {}
    return {}


def _fetch_business_id_bots_override(bid: int, bot_pk: int, bot_name: str) -> dict:
    """JSON `config` from livekitvoicebot_cluster.business_id_bots (dynamic per tenant/bot)."""
    override_cfg: dict = {}
    try:
        with connections["cluster"].cursor() as cur:
            cur.execute(
                """
                SELECT config FROM business_id_bots
                WHERE business_id = %s AND (name = %s OR name = %s)
                ORDER BY updated_at DESC
                LIMIT 1
                """,
                [bid, str(bot_pk), bot_name],
            )
            r = cur.fetchone()
            if r and r[0] is not None:
                override_cfg = _json_dict(r[0])
    except Exception:
        override_cfg = {}
    return override_cfg


def _pick_str(override: dict, adv: dict, data: dict, *keys: str) -> str:
    """First non-empty among override JSON, advanced_settings JSON, then bot row column."""
    for d in (override, adv, data):
        for k in keys:
            if k not in d or d.get(k) is None:
                continue
            s = str(d.get(k)).strip()
            if s:
                return s
    return ""


@api_view(["GET"])
@permission_classes([AllowAny])
def cluster_bot_mcube_config(request, business_id: int, bot_id: int):
    """
    Internal helper endpoint for MCube runtime.

    Reads `{business_id}_bots` on cluster DB plus JSON overrides from `business_id_bots`
    (same database: livekitvoicebot_cluster). Returns fields the WS bridge / outbound worker
    need for prompts, providers, models, voice, MCube exenumber/gid.
    """
    try:
        bid = int(business_id)
        bot_pk = int(bot_id)
    except Exception:
        return Response({"error": "Invalid business_id or bot_id"}, status=status.HTTP_400_BAD_REQUEST)

    ensure_business_cluster_tables(bid)
    table = f"{bid}_bots"
    data: dict = {}

    with connections["cluster"].cursor() as cur:
        cur.execute(f"SELECT * FROM {_q_ident(table)} WHERE bot_id = %s LIMIT 1", [bot_pk])
        row = cur.fetchone()
        if row:
            cols = [c[0] for c in cur.description]
            data = dict(zip(cols, row))

    bot_name = str(data.get("bot_name") or data.get("name") or "").strip()
    override_cfg = _fetch_business_id_bots_override(bid, bot_pk, bot_name)

    # If no row in `{bid}_bots`, allow JSON-only config in `business_id_bots` (name = bot id string).
    if not data and override_cfg:
        data = {}

    if not data and not override_cfg:
        return Response({"error": "Bot not found"}, status=status.HTTP_404_NOT_FOUND)

    adv = _json_dict(data.get("advanced_settings"))

    # prompt column is the bot system prompt in cluster tables
    system_prompt = _pick_str(override_cfg, adv, data, "system_prompt", "MCUBE_SYSTEM_PROMPT", "prompt")
    system_prompt = str(system_prompt).replace("\\n", "\n").strip()

    voice_id = ""
    voice_raw = data.get("voice") if data else None
    if isinstance(voice_raw, (bytes, bytearray)):
        voice_raw = voice_raw.decode("utf-8", errors="replace")
    if isinstance(voice_raw, str) and voice_raw.strip():
        try:
            voice_obj = json.loads(voice_raw)
            if isinstance(voice_obj, dict):
                voice_id = str(voice_obj.get("voice_id") or voice_obj.get("voiceId") or "").strip()
        except Exception:
            voice_id = ""

    first_msg = _pick_str(override_cfg, adv, data, "first_message", "MCUBE_FIRST_MESSAGE", "message_outbound")
    if not first_msg and data.get("message_outbound"):
        first_msg = str(data.get("message_outbound") or "").strip()

    return Response(
        {
            "business_id": bid,
            "bot_id": bot_pk,
            "system_prompt": system_prompt,
            "first_message": first_msg,
            "message_outbound": str(data.get("message_outbound") or "").strip() if data else "",
            "message_inbound": str(data.get("message_inbound") or "").strip() if data else "",
            "llm_model": _pick_str(override_cfg, adv, data, "llm_model", "MCUBE_LLM_MODEL"),
            "llm_provider": _pick_str(override_cfg, adv, data, "llm_provider", "MCUBE_LLM_PROVIDER"),
            "stt_provider": _pick_str(override_cfg, adv, data, "stt_provider", "MCUBE_STT_PROVIDER"),
            "stt_model_id": _pick_str(override_cfg, adv, data, "stt_model_id", "MCUBE_STT_MODEL_ID"),
            "stt_language_code": _pick_str(
                override_cfg, adv, data, "stt_language_code", "MCUBE_STT_LANGUAGE_CODE", "default_language"
            ),
            "tts_provider": _pick_str(override_cfg, adv, data, "tts_provider", "MCUBE_TTS_PROVIDER"),
            "tts_model": _pick_str(override_cfg, adv, data, "tts_model", "MCUBE_TTS_MODEL"),
            "tts_voice_id": _pick_str(override_cfg, adv, data, "tts_voice_id", "MCUBE_TTS_VOICE_ID") or voice_id,
            "tts_encoding": _pick_str(override_cfg, adv, data, "tts_encoding", "MCUBE_TTS_ENCODING"),
            "tts_chunk_ms": _pick_str(override_cfg, adv, data, "tts_chunk_ms", "MCUBE_TTS_CHUNK_MS"),
            "tts_gain": _pick_str(override_cfg, adv, data, "tts_gain", "MCUBE_TTS_GAIN"),
            "playback_pace_factor": _pick_str(override_cfg, adv, data, "playback_pace_factor", "MCUBE_PLAYBACK_PACE_FACTOR"),
            "checkpoint_every": _pick_str(override_cfg, adv, data, "checkpoint_every", "MCUBE_CHECKPOINT_EVERY"),
            "mcube_exenumber": _pick_str(override_cfg, adv, data, "mcube_exenumber"),
            "mcube_gid": _pick_str(override_cfg, adv, data, "mcube_gid"),
            "platform_settings": data.get("platform_settings") if data else None,
            "conversation_behavior": data.get("conversation_behavior") if data else None,
        }
    )
