"""LLM advisory answers for Avatar general chat (cloud orchestra primary, local Ollama fallback)."""

from __future__ import annotations

import json
import logging
import os
from typing import Any
from urllib.error import HTTPError, URLError
from urllib.request import Request, urlopen

from iuve_connect_agents.shared.feature_flags import (
    avatar_llm_advisory_enabled,
    avatar_local_ollama_fallback_enabled,
    avatar_local_ollama_reachable,
    avatar_provider_orchestra_enabled,
)

logger = logging.getLogger(__name__)

DEFAULT_OLLAMA_BASE_URL = "http://localhost:11434"
DEFAULT_ADVISORY_MODEL = "phi3:latest"
DEFAULT_ADVISORY_TIMEOUT_SEC = 90

_ADVISORY_SYSTEM_PROMPT = (
    "You are IuVe Avatar, a concise assistant on a Linux desktop. "
    "Answer in the same language as the user. "
    "For arithmetic, compute and state the numeric result. "
    "For general knowledge, give a brief accurate answer. "
    "Do not mention device permissions unless the user asks to perform a device action."
)


def advisory_ollama_model() -> str:
    raw = os.getenv("OLLAMA_FAST_MODEL", DEFAULT_ADVISORY_MODEL).strip()
    return raw or DEFAULT_ADVISORY_MODEL


def advisory_ollama_base_url() -> str:
    raw = os.getenv("OLLAMA_BASE_URL", DEFAULT_OLLAMA_BASE_URL).strip()
    return raw.rstrip("/") or DEFAULT_OLLAMA_BASE_URL


def advisory_ollama_timeout_sec() -> int:
    raw = os.getenv("OLLAMA_AUX_CALL_TIMEOUT_SEC")
    if raw is None:
        return DEFAULT_ADVISORY_TIMEOUT_SEC
    try:
        return max(30, int(raw))
    except ValueError:
        return DEFAULT_ADVISORY_TIMEOUT_SEC


def fetch_avatar_llm_advisory_response(text: str, *, trust_state: str = "") -> str | None:
    """Return an LLM advisory answer for general chat, or None when unavailable."""
    query = str(text or "").strip()
    if not query or not avatar_llm_advisory_enabled(trust_state=trust_state):
        return None

    try:
        from iuve_connect_agents.desktop.active.device_capability_scanner import is_device_inventory_question

        if is_device_inventory_question(query):
            return None
    except ImportError:
        pass

    answer = _fetch_via_cloud_api(query, trust_state=trust_state)
    if answer:
        return answer

    answer = _fetch_via_orchestra(query)
    if answer:
        try:
            from iuve_connect_agents.desktop.orchestra_routing_tracker import (
                OrchestraRoutingPath,
                record_routing_path,
            )

            record_routing_path(OrchestraRoutingPath.CLOUD_ORCHESTRA)
        except ImportError:
            pass
        return answer

    if avatar_local_ollama_fallback_enabled() and avatar_local_ollama_reachable():
        answer = _fetch_via_local_ollama(query)
        if answer:
            try:
                from iuve_connect_agents.desktop.orchestra_routing_tracker import (
                    OrchestraRoutingPath,
                    record_routing_path,
                )

                record_routing_path(OrchestraRoutingPath.LOCAL_OLLAMA)
            except ImportError:
                pass
        return answer
    try:
        from iuve_connect_agents.desktop.orchestra_routing_tracker import (
            OrchestraRoutingPath,
            record_routing_path,
        )

        record_routing_path(OrchestraRoutingPath.UNAVAILABLE)
    except ImportError:
        pass
    return None


def _fetch_via_cloud_api(text: str, *, trust_state: str = "") -> str | None:
    try:
        from iuve_connect_agents.shared.avatar_llm_cloud import request_cloud_advisory
    except ImportError:
        return None
    return request_cloud_advisory(text, trust_state=trust_state)


def _fetch_via_orchestra(text: str) -> str | None:
    if not avatar_provider_orchestra_enabled():
        return None
    try:
        from app.provider_orchestra.intent_router import classify_orchestra_intent
        from app.provider_orchestra.orchestra_service import orchestra_service
        from app.provider_orchestra.types import OrchestraRequest
    except ImportError:
        return None

    try:
        if not orchestra_service.is_enabled():
            return None

        intent = classify_orchestra_intent(text)
        if intent.intent.value not in {"dialogue", "reasoning", "unknown"}:
            return None

        response = orchestra_service.route_request(OrchestraRequest(user_id="", text=text), db=None)
        payload = orchestra_service.invoke_for_execution(
            fallback_chain=list(response.decision.fallback_chain or []),
            sub_model=response.decision.selected_sub_model,
            system_prompt=_ADVISORY_SYSTEM_PROMPT,
            context=[{"role": "user", "content": text}],
        )
        if payload.get("success"):
            content = str(payload.get("content") or "").strip()
            if content:
                return content
    except Exception:
        logger.exception("avatar_llm_advisory_orchestra_failed")
    return None


def _fetch_via_local_ollama(text: str) -> str | None:
    messages = [
        {"role": "system", "content": _ADVISORY_SYSTEM_PROMPT},
        {"role": "user", "content": text},
    ]
    try:
        response = _post_ollama_chat(
            base_url=advisory_ollama_base_url(),
            model=advisory_ollama_model(),
            messages=messages,
            timeout_sec=advisory_ollama_timeout_sec(),
        )
    except (HTTPError, URLError, TimeoutError, ValueError) as exc:
        logger.warning("avatar_llm_advisory_ollama_failed error=%s", exc)
        return None

    content = _extract_message_content(response)
    return content or None


def _post_ollama_chat(
    *,
    base_url: str,
    model: str,
    messages: list[dict[str, Any]],
    timeout_sec: int,
) -> dict[str, Any]:
    url = f"{base_url.rstrip('/')}/api/chat"
    body = json.dumps(
        {
            "model": model,
            "messages": messages,
            "stream": False,
        }
    ).encode("utf-8")
    request = Request(
        url,
        data=body,
        headers={"Content-Type": "application/json"},
        method="POST",
    )
    with urlopen(request, timeout=max(5, int(timeout_sec))) as response:
        raw = response.read().decode("utf-8")
    payload = json.loads(raw)
    if not isinstance(payload, dict):
        raise ValueError("ollama_response_not_object")
    return payload


def _extract_message_content(response: dict[str, Any]) -> str:
    message = response.get("message")
    if isinstance(message, dict):
        return str(message.get("content") or "").strip()
    return str(response.get("response") or "").strip()
