"""PixelRAG VLM route readiness.

This module checks approved Ollama routes for installed VLM candidate models.
By default it only reads /api/tags. Execute preflight can opt into a tiny
/api/generate probe to avoid blindly sending screenshot tiles to a route that
is installed but cannot generate.
"""

from __future__ import annotations

import os
import time
from datetime import datetime, timezone
from typing import Any

import requests

from services.ollama_service import (
    OLLAMA_HOST_FALLBACK,
    OLLAMA_HOST_PRIMARY,
    OLLAMA_HOST_PRIMARY_PROXY,
    OLLAMA_HOST_SECONDARY,
    OLLAMA_HOST_SECONDARY_PROXY,
    get_host_label,
    get_provider_tag,
)


POLICY = "read_only_pixelrag_vlm_route_readiness_v1"
DEFAULT_TIMEOUT_SECONDS = 3
DEFAULT_PROBE_TIMEOUT_SECONDS = 8
DEFAULT_PROBE_PROMPT = "Return exactly OK."
DEFAULT_MODEL = (
    os.getenv("PIXELRAG_VLM_MODEL")
    or os.getenv("PPT_VISION_MODEL")
    or "minicpm-v:latest"
)
VISION_MODEL_CANDIDATES = (
    "minicpm-v:latest",
    "gemma3:4b",
    "llava:latest",
)


def _unique(values: list[str] | tuple[str, ...]) -> list[str]:
    seen: set[str] = set()
    result: list[str] = []
    for value in values:
        clean = str(value or "").strip()
        if not clean or clean in seen:
            continue
        seen.add(clean)
        result.append(clean)
    return result


def _approved_hosts() -> list[str]:
    return _unique((
        OLLAMA_HOST_PRIMARY,
        OLLAMA_HOST_PRIMARY_PROXY,
        OLLAMA_HOST_SECONDARY,
        OLLAMA_HOST_SECONDARY_PROXY,
        OLLAMA_HOST_FALLBACK,
    ))


def _candidate_models(configured_model: str) -> list[str]:
    env_candidates = [
        item.strip()
        for item in os.getenv("PIXELRAG_VLM_MODEL_CANDIDATES", "").split(",")
        if item.strip()
    ]
    return _unique((configured_model, *env_candidates, *VISION_MODEL_CANDIDATES))


def _fetch_models(host: str, timeout: int) -> dict[str, Any]:
    clean_host = str(host or "").rstrip("/")
    item: dict[str, Any] = {
        "host": clean_host,
        "host_label": get_host_label(clean_host),
        "provider": get_provider_tag(clean_host),
        "reachable": False,
        "model_count": 0,
        "models": [],
        "error": "",
    }
    try:
        response = requests.get(f"{clean_host}/api/tags", timeout=max(1, timeout))
        item["http_status"] = response.status_code
        if response.status_code != 200:
            item["error"] = f"HTTP {response.status_code}: {response.text[:180]}"
            return item
        payload = response.json()
        models = [
            str(model.get("name") or "").strip()
            for model in list(payload.get("models") or [])
            if str(model.get("name") or "").strip()
        ]
        item.update({
            "reachable": True,
            "model_count": len(models),
            "models": models[:80],
        })
    except Exception as exc:
        item["error"] = f"{type(exc).__name__}: {str(exc)[:180]}"
    return item


def _probe_generate(
    host: str,
    *,
    model: str,
    timeout: int,
    prompt: str,
) -> dict[str, Any]:
    clean_host = str(host or "").rstrip("/")
    started = time.monotonic()
    result: dict[str, Any] = {
        "generate_probe_performed": True,
        "generate_probe_model": str(model or "").strip(),
        "generate_probe_ok": False,
        "generate_probe_error": "",
        "generate_probe_duration_ms": 0,
    }
    payload = {
        "model": str(model or "").strip(),
        "prompt": str(prompt or DEFAULT_PROBE_PROMPT),
        "stream": False,
        "options": {
            "temperature": 0,
            "num_predict": 8,
            "num_ctx": 512,
        },
        "keep_alive": "1m",
    }
    try:
        response = requests.post(
            f"{clean_host}/api/generate",
            json=payload,
            timeout=max(1, timeout),
        )
        result["generate_probe_http_status"] = response.status_code
        if response.status_code != 200:
            result["generate_probe_error"] = (
                f"HTTP {response.status_code}: {response.text[:180]}"
            )
            return result
        try:
            body = response.json()
        except Exception as exc:
            result["generate_probe_error"] = f"invalid_json: {type(exc).__name__}"
            return result
        result["generate_probe_ok"] = (
            isinstance(body, dict)
            and (
                "response" in body
                or bool(body.get("done"))
                or bool(body.get("context"))
            )
        )
        if not result["generate_probe_ok"]:
            result["generate_probe_error"] = "generate_response_missing_output_fields"
    except Exception as exc:
        result["generate_probe_error"] = f"{type(exc).__name__}: {str(exc)[:180]}"
    finally:
        result["generate_probe_duration_ms"] = int((time.monotonic() - started) * 1000)
    return result


def build_pixelrag_vlm_route_readiness(
    *,
    model: str | None = None,
    timeout_seconds: int | None = None,
    include_models: bool = False,
    probe_generate: bool = False,
    probe_timeout_seconds: int | None = None,
    probe_prompt: str | None = None,
) -> dict[str, Any]:
    """Read approved Ollama tags and recommend an installed VLM model."""
    configured_model = str(model or DEFAULT_MODEL).strip() or DEFAULT_MODEL
    timeout = max(1, min(int(timeout_seconds or DEFAULT_TIMEOUT_SECONDS), 20))
    probe_timeout = max(
        1,
        min(int(probe_timeout_seconds or DEFAULT_PROBE_TIMEOUT_SECONDS), 30),
    )
    generated_at = datetime.now(timezone.utc).isoformat()
    candidates = _candidate_models(configured_model)
    host_results = [_fetch_models(host, timeout) for host in _approved_hosts()]

    reachable_hosts = [item for item in host_results if item.get("reachable")]
    configured_hosts = [
        item for item in reachable_hosts
        if configured_model in set(item.get("models") or [])
    ]
    selected_model = ""
    selected_host = ""
    selected_provider = ""
    selected_reason = ""
    for candidate in candidates:
        for host in reachable_hosts:
            if candidate not in set(host.get("models") or []):
                continue
            selected_model = candidate
            selected_host = str(host.get("host") or "")
            selected_provider = str(host.get("provider") or "")
            selected_reason = (
                "configured_model_available"
                if candidate == configured_model
                else "installed_candidate_fallback"
            )
            break
        if selected_model:
            break

    tag_model_route_ready = bool(selected_model and selected_host)
    generate_probe_performed = bool(probe_generate and tag_model_route_ready)
    generate_probe_ok_count = 0
    generate_ready_model = ""
    generate_ready_host = ""
    generate_ready_provider = ""
    generate_route_ready: bool | None = None
    if generate_probe_performed:
        generate_route_ready = False
        for host in reachable_hosts:
            if selected_model not in set(host.get("models") or []):
                continue
            probe_result = _probe_generate(
                str(host.get("host") or ""),
                model=selected_model,
                timeout=probe_timeout,
                prompt=probe_prompt or DEFAULT_PROBE_PROMPT,
            )
            host.update(probe_result)
            if probe_result.get("generate_probe_ok"):
                generate_probe_ok_count += 1
                if not generate_ready_host:
                    generate_ready_model = selected_model
                    generate_ready_host = str(host.get("host") or "")
                    generate_ready_provider = str(host.get("provider") or "")
        if generate_ready_host:
            selected_host = generate_ready_host
            selected_provider = generate_ready_provider
            generate_route_ready = True

    model_route_ready = tag_model_route_ready and (
        bool(generate_route_ready) if generate_probe_performed else True
    )
    if not reachable_hosts:
        status = "critical"
        next_action = "repair_approved_ollama_host_connectivity"
    elif not tag_model_route_ready:
        status = "critical"
        next_action = "install_or_configure_pixelrag_vlm_model_on_approved_ollama_host"
    elif generate_probe_performed and not generate_route_ready:
        status = "critical"
        next_action = "repair_ollama_vlm_generate_runtime_or_proxy_timeout"
    elif selected_model != configured_model:
        status = "warning"
        next_action = "run_pixelrag_vlm_replay_worker_execute_with_selected_model"
    else:
        status = "ok"
        next_action = "run_pixelrag_vlm_replay_worker_execute"

    public_host_results: list[dict[str, Any]] = []
    for item in host_results:
        result = dict(item)
        result["configured_model_available"] = configured_model in set(item.get("models") or [])
        result["candidate_models_available"] = [
            candidate for candidate in candidates
            if candidate in set(item.get("models") or [])
        ]
        result.setdefault("generate_probe_performed", False)
        result.setdefault("generate_probe_model", "")
        result.setdefault("generate_probe_ok", False)
        result.setdefault("generate_probe_error", "")
        result.setdefault("generate_probe_duration_ms", 0)
        if not include_models:
            result.pop("models", None)
        public_host_results.append(result)

    summary = {
        "host_count": len(host_results),
        "reachable_host_count": len(reachable_hosts),
        "configured_model": configured_model,
        "configured_model_available_count": len(configured_hosts),
        "candidate_model": selected_model,
        "candidate_host": selected_host,
        "candidate_provider": selected_provider,
        "candidate_selection_reason": selected_reason,
        "tag_model_route_ready": tag_model_route_ready,
        "model_route_ready": model_route_ready,
        "generate_probe_performed": generate_probe_performed,
        "generate_probe_ok_count": generate_probe_ok_count,
        "generate_ready_model": generate_ready_model,
        "generate_ready_host": generate_ready_host,
        "generate_ready_provider": generate_ready_provider,
        "generate_route_ready": generate_route_ready,
        "model_call_performed": generate_probe_performed,
        "writes_database_count": 0,
        "primary_human_gate_count": 0,
    }
    return {
        "success": status != "critical",
        "policy": POLICY,
        "status": status,
        "generated_at": generated_at,
        "timeout_seconds": timeout,
        "probe_timeout_seconds": probe_timeout if probe_generate else 0,
        "configured_model": configured_model,
        "candidate_models": candidates,
        "summary": summary,
        "hosts": public_host_results,
        "controlled_apply": {
            "network_call": True,
            "model_call": generate_probe_performed,
            "db_write": False,
            "writes_database": False,
            "writes_database_count": 0,
            "secret_read": False,
            "production_price_write": False,
            "primary_human_gate_count": 0,
        },
        "next_machine_action": next_action,
    }


__all__ = [
    "POLICY",
    "build_pixelrag_vlm_route_readiness",
]
