"""Gemini CLI wrapper for independent content quality evaluation.

Uses the Gemini CLI (google/gemini-cli) to evaluate raw crawled content
before distillation, providing third-party quality assessment.

Requires: `npm install -g @google/gemini-cli` and Google auth configured.
"""

from __future__ import annotations

import json
import logging
import re
import subprocess
from typing import Optional

logger = logging.getLogger("refcurator.gemini")

GEMINI_CMD = "gemini"
TIMEOUT_SECONDS = 60

EVALUATION_PROMPT = """You are evaluating a raw reference document for inclusion in a curated knowledge base.

Topic: {topic}
Source URL: {source_url}

Score each criterion from 0.0 to 1.0:
- relevance: Does this content actually relate to the topic "{topic}"?
- authority: Is this from an authoritative, official source (official docs, research paper) or low-quality (scraped blog, forum post, SEO spam)?
- completeness: Is this a complete article with substance, or a navigation fragment, error page, stub, or boilerplate?
- freshness: Does the information appear current and not outdated? Look for version numbers, dates, deprecated APIs.
- distill_value: Does this contain unique, valuable information worth summarizing, or is it redundant with what official docs already cover?

Return ONLY a valid JSON object with no markdown formatting, no code fences, no explanation:
{{"relevance": 0.0, "authority": 0.0, "completeness": 0.0, "freshness": 0.0, "distill_value": 0.0, "verdict": "approve", "reason": "brief explanation"}}

The verdict must be one of: "approve", "reject", "deep_research"
- approve: source is worth distilling (score >= 0.75 typical)
- reject: not worth distilling (low quality, irrelevant, or fragment)
- deep_research: partially relevant but needs supplementary sources"""


def is_available() -> bool:
    """Check if the Gemini CLI is installed and authenticated."""
    try:
        result = subprocess.run(
            [GEMINI_CMD, "--help"],
            capture_output=True, timeout=10,
        )
        return result.returncode == 0
    except (FileNotFoundError, subprocess.TimeoutExpired):
        return False


def evaluate_content(
    content: str,
    topic: str,
    source_url: str = "",
    timeout: int = TIMEOUT_SECONDS,
) -> Optional[dict]:
    """Evaluate raw content using Gemini CLI.

    Args:
        content: Raw document content (markdown/text)
        topic: The curation topic for relevance scoring
        source_url: Original URL of the content
        timeout: Subprocess timeout in seconds

    Returns:
        Parsed evaluation dict with scores, verdict, and reason.
        Returns None if Gemini is unavailable or evaluation fails.
    """
    # Truncate very long content to avoid overwhelming the model
    max_chars = 50_000
    if len(content) > max_chars:
        content = content[:max_chars] + "\n\n[... content truncated for evaluation ...]"

    prompt = EVALUATION_PROMPT.format(topic=topic, source_url=source_url)

    try:
        result = subprocess.run(
            [GEMINI_CMD, prompt],
            input=content,
            capture_output=True,
            text=True,
            timeout=timeout,
        )

        if result.returncode != 0:
            logger.warning("Gemini CLI failed (exit %d): %s", result.returncode, result.stderr[:200])
            return None

        return _parse_response(result.stdout)

    except FileNotFoundError:
        logger.warning("Gemini CLI not found. Install with: npm install -g @google/gemini-cli")
        return None
    except subprocess.TimeoutExpired:
        logger.warning("Gemini CLI timed out after %ds", timeout)
        return None
    except Exception as e:
        logger.warning("Gemini evaluation failed: %s", e)
        return None


def _parse_response(output: str) -> Optional[dict]:
    """Parse Gemini's response, handling markdown-wrapped JSON."""
    text = output.strip()

    # Try direct JSON parse first
    try:
        return _validate_evaluation(json.loads(text))
    except json.JSONDecodeError:
        pass

    # Try extracting JSON from markdown code fences
    m = re.search(r"```(?:json)?\s*\n?(.*?)\n?```", text, re.DOTALL)
    if m:
        try:
            return _validate_evaluation(json.loads(m.group(1).strip()))
        except json.JSONDecodeError:
            pass

    # Try finding a JSON object anywhere in the output
    m = re.search(r"\{[^{}]*\"relevance\"[^{}]*\}", text, re.DOTALL)
    if m:
        try:
            return _validate_evaluation(json.loads(m.group(0)))
        except json.JSONDecodeError:
            pass

    logger.warning("Could not parse Gemini response as JSON: %s", text[:200])
    return None


def _validate_evaluation(data: dict) -> Optional[dict]:
    """Validate that the evaluation has required fields and reasonable values."""
    required_scores = ["relevance", "authority", "completeness", "freshness", "distill_value"]

    for field in required_scores:
        if field not in data:
            logger.warning("Missing required field: %s", field)
            return None
        score = data[field]
        if not isinstance(score, (int, float)) or score < 0 or score > 1:
            logger.warning("Invalid score for %s: %s", field, score)
            return None

    if "verdict" not in data or data["verdict"] not in ("approve", "reject", "deep_research"):
        data["verdict"] = _derive_verdict(data)

    if "reason" not in data:
        data["reason"] = ""

    # Add weighted score
    data["weighted_score"] = round(
        data["relevance"] * 0.25
        + data["authority"] * 0.25
        + data["completeness"] * 0.20
        + data["freshness"] * 0.15
        + data["distill_value"] * 0.15,
        4,
    )

    return data


def _derive_verdict(data: dict) -> str:
    """Derive verdict from scores if Gemini didn't provide one."""
    score = (
        data.get("relevance", 0) * 0.25
        + data.get("authority", 0) * 0.25
        + data.get("completeness", 0) * 0.20
        + data.get("freshness", 0) * 0.15
        + data.get("distill_value", 0) * 0.15
    )
    if score >= 0.75:
        return "approve"
    elif score >= 0.50:
        return "deep_research"
    else:
        return "reject"
