"""Content filter soften-prompt retry via Gemini Flash.

When a generation API returns a content filter block (HTTP 400 or safety
error in body), run.shot calls soften_prompt() to rewrite the prompt with
safer language and retry once.

Detection: needs_softening() uses case-insensitive regex matching for known
content filter keywords.

Softening: soften_prompt() calls Gemini Flash via google.genai (lazy singleton
client, same pattern as validation.py). Returns softened prompt or None on
failure (escalates to review queue).

Eligibility: is_model_soften_eligible() checks model_profiles.json for
requires_content_filter_soften_retry field.

Audit: All attempts are logged to soften_log.jsonl.
"""

from __future__ import annotations

import json
import logging
import os
import re
from datetime import datetime, timezone
from pathlib import Path
from typing import Any

logger = logging.getLogger(__name__)

# Lazy singleton GenAI client
_CLIENT = None

# Case-insensitive patterns that indicate content filter blocks
_CONTENT_FILTER_PATTERNS = [
    re.compile(r"content.?filter", re.IGNORECASE),
    re.compile(r"safety.?blocked", re.IGNORECASE),
    re.compile(r"prohibited", re.IGNORECASE),
    re.compile(r"content.?policy", re.IGNORECASE),
    re.compile(r"blocked.?prompt", re.IGNORECASE),
    re.compile(r"responsible.?ai", re.IGNORECASE),
]

_SOFTEN_TEMPLATE = """You are a visual production prompt rewriter. The following prompt was blocked by a content filter.

Rewrite the prompt to preserve the SAME visual composition, character actions, and narrative meaning while using language that will pass content moderation filters. Rules:
- Keep all character names, locations, and shot descriptions
- Replace violent/explicit verbs with neutral equivalents
- Remove gore, blood, or graphic injury descriptions
- Maintain dramatic tension through implication, not depiction
- Output ONLY the rewritten prompt, no explanation

Original prompt:
{original_prompt}

Rewritten prompt:"""


def needs_softening(error_text: str, http_status: int | None = None) -> bool:
    """Detect whether an API error indicates a content filter block.

    Args:
        error_text: The error message or response body from the API.
        http_status: The HTTP status code, if available.

    Returns:
        True if the error looks like a content filter block.
    """
    if http_status == 400:
        return True
    for pattern in _CONTENT_FILTER_PATTERNS:
        if pattern.search(error_text):
            return True
    return False


def _get_genai_client():
    """Lazy-initialize Gemini client (singleton)."""
    global _CLIENT
    if _CLIENT is None:
        from google import genai
        key = os.environ.get("GEMINI_API_KEY") or os.environ.get("GOOGLE_API_KEY")
        if not key:
            raise RuntimeError("GEMINI_API_KEY or GOOGLE_API_KEY not set")
        _CLIENT = genai.Client(api_key=key)
    return _CLIENT


def _get_audit_log_path() -> Path:
    """Return path to the soften audit log.

    Default: recoil/pipeline/feedback/soften_log.jsonl (overridable for testing).
    """
    return Path(__file__).parent.parent / "feedback" / "soften_log.jsonl"


def _log_attempt(
    log_path: Path,
    shot_id: str,
    original_prompt: str,
    softened_prompt: str | None,
    model: str,
    success: bool,
) -> None:
    """Append a soften attempt to the audit log."""
    log_path.parent.mkdir(parents=True, exist_ok=True)
    entry = {
        "shot_id": shot_id,
        "original_prompt": original_prompt,
        "softened_prompt": softened_prompt,
        "model": model,
        "success": success,
        "timestamp": datetime.now(timezone.utc).isoformat().replace("+00:00", "Z"),
    }
    with log_path.open("a") as f:
        f.write(json.dumps(entry, separators=(",", ":")) + "\n")


def soften_prompt(
    original_prompt: str,
    shot_id: str,
    model: str,
) -> str | None:
    """Rewrite a prompt to pass content filters using Gemini Flash.

    Args:
        original_prompt: The prompt that was blocked.
        shot_id: Shot identifier for audit logging.
        model: The target generation model (for audit logging).

    Returns:
        The softened prompt string, or None if the rewrite fails (caller
        should escalate to review queue).
    """
    log_path = _get_audit_log_path()
    try:
        client = _get_genai_client()
        prompt = _SOFTEN_TEMPLATE.format(original_prompt=original_prompt)
        response = client.models.generate_content(
            model="gemini-2.0-flash",
            contents=prompt,
        )
        softened = response.text.strip()
        if not softened:
            _log_attempt(log_path, shot_id, original_prompt, None, model, False)
            return None
        _log_attempt(log_path, shot_id, original_prompt, softened, model, True)
        return softened
    except Exception:
        logger.exception("soften_prompt failed for %s", shot_id)
        _log_attempt(log_path, shot_id, original_prompt, None, model, False)
        return None


def is_model_soften_eligible(model_id: str) -> bool:
    """Check if a model requires content filter soften retry.

    Reads requires_content_filter_soften_retry from model_profiles.json.
    Returns False for unknown models (safe default).
    """
    try:
        from recoil.core.model_profiles import get_profile
        profile = get_profile(model_id)
        return profile.get("requires_content_filter_soften_retry", False)
    except (KeyError, ImportError):
        return False
