"""Unified LLM caller — dispatches to Gemini / OpenRouter / OpenAI-compatible.

Python port of the project's PHP callOpenRouter()/callLLM(). Usage:

    from apps.llm.service import call_llm
    result = call_llm("Say hello", purpose="test", user=request.user)
    if result["ok"]:
        text = result["text"]
"""
import json
import urllib.request

from .models import LLMProvider, LLMCallLog


import os
from django.conf import settings

from .models import LLMProvider, LLMCallLog


def _resolve(provider: LLMProvider | None = None, provider_type: str | None = None) -> LLMProvider | None:
    if provider:
        return provider

    target_type = (provider_type or "").upper()
    if target_type:
        p = LLMProvider.objects.filter(is_active=True, provider=target_type).first()
        if p:
            return p

    # Prefer active GEMINI provider first
    gemini = LLMProvider.objects.filter(is_active=True, provider=LLMProvider.Provider.GEMINI).first()
    if gemini:
        return gemini

    # Check environment variable for GEMINI_API_KEY
    env_key = (os.getenv("GEMINI_API_KEY", "") or getattr(settings, "GEMINI_API_KEY", "") or "").strip()
    if env_key or target_type == "GEMINI":
        try:
            p, _ = LLMProvider.objects.get_or_create(
                provider=LLMProvider.Provider.GEMINI,
                defaults={
                    "name": "Google Gemini",
                    "model": os.getenv("GEMINI_MODEL", "gemini-2.0-flash"),
                    "max_tokens": 1200,
                    "temperature": 0.7,
                    "is_active": True,
                    "is_default": True,
                }
            )
            if env_key and not p.api_key:
                p.set_api_key(env_key)
                p.save()
            return p
        except Exception:
            pass

    return (LLMProvider.objects.filter(is_active=True, is_default=True).first()
            or LLMProvider.objects.filter(is_active=True).first())


def _post(url, headers, payload, timeout):
    data = json.dumps(payload).encode()
    req = urllib.request.Request(url, data=data, headers=headers, method="POST")
    with urllib.request.urlopen(req, timeout=timeout) as resp:
        return json.loads(resp.read().decode())


def call_llm(
    prompt: str,
    provider: LLMProvider | None = None,
    provider_type: str | None = None,
    purpose: str = "",
    user=None,
    timeout: int = 40,
) -> dict:
    """Returns {ok, text, error, provider}. Logs every real network call."""
    p = _resolve(provider, provider_type)
    if not p:
        return {"ok": False, "text": "", "error": "No active LLM provider configured.", "provider": None}

    key = p.api_key
    if not key and p.provider != LLMProvider.Provider.CUSTOM:
        return {"ok": False, "text": "", "error": f"Provider '{p.name}' has no API key.", "provider": p.name}

    text, error, ok = "", "", False
    try:
        if p.provider == LLMProvider.Provider.GEMINI:
            base = p.base_url or "https://generativelanguage.googleapis.com/v1beta/models"
            url = f"{base}/{p.model}:generateContent"
            body = {"contents": [{"parts": [{"text": prompt}]}],
                    "generationConfig": {"temperature": p.temperature, "maxOutputTokens": p.max_tokens}}
            data = _post(url, {"Content-Type": "application/json", "X-goog-api-key": key}, body, timeout)
            parts = (data.get("candidates") or [{}])[0].get("content", {}).get("parts", [])
            text = "".join(part.get("text", "") for part in parts).strip()
            ok = bool(text)
            if not ok:
                error = data.get("error", {}).get("message", "Empty response from Gemini.")

        else:  # OPENROUTER / OPENAI / CUSTOM — all OpenAI-compatible chat/completions
            base = p.base_url or ("https://openrouter.ai/api/v1" if p.provider == LLMProvider.Provider.OPENROUTER
                                  else "https://api.openai.com/v1")
            url = f"{base.rstrip('/')}/chat/completions"
            headers = {"Content-Type": "application/json", "Authorization": f"Bearer {key}"}
            if p.provider == LLMProvider.Provider.OPENROUTER:
                headers["HTTP-Referer"] = "http://localhost:3000"
                headers["X-Title"] = "TA-ATS"
            body = {"model": p.model, "max_tokens": p.max_tokens, "temperature": p.temperature,
                    "messages": [{"role": "user", "content": prompt}]}
            data = _post(url, headers, body, timeout)
            text = ((data.get("choices") or [{}])[0].get("message", {}).get("content", "") or "").strip()
            ok = bool(text)
            if not ok:
                error = data.get("error", {}).get("message", "Empty response from provider.")
    except Exception as e:
        error = str(e)
        ok = False

    try:
        LLMCallLog.objects.create(
            provider=p, purpose=purpose, ok=ok, error=error[:500],
            prompt_chars=len(prompt), response_chars=len(text), called_by=user,
        )
    except Exception:
        pass

    return {"ok": ok, "text": text, "error": error, "provider": p.name}
