"""Gemini-powered prompt formatter for AiCippy.

Structures, translates, and reframes user prompts before sending to the
odin agent. Single-use per toggle — auto-disables after each execution.

Flow:
  1. User toggles formatter ON (F3 / arrow / /formatter)
  2. CLI checks tenant secret for Gemini API key via api.aivibe.cloud
     - If found and valid → use it
     - If not found or invalid → prompt user for key, save to tenant secrets
  3. Gemini formats/structures/translates the next prompt
  4. Formatted prompt shown to user for confirmation
  5. On approval, sent to odin agent
  6. Formatter auto-disables (single-use per toggle)

API key is one-time per tenant — stored encrypted in RDS via the platform
API with tenant-level RLS. Other tenants cannot access the key.
"""

from __future__ import annotations

from typing import TYPE_CHECKING, Final

import httpx

from aicippy.config import get_settings
from aicippy.utils.logging import get_logger

if TYPE_CHECKING:
    from aicippy.platform import PlatformClient

logger = get_logger(__name__)

# Gemini API configuration (URLs loaded from centralized config)
_settings = get_settings()
GEMINI_API_URL: Final[str] = _settings.gemini_api_url
GEMINI_VALIDATE_URL: Final[str] = _settings.gemini_models_url
TENANT_CREDENTIAL_NAME: Final[str] = "gemini_api_key"
HTTP_OK: Final[int] = 200


FORMATTER_SYSTEM_PROMPT: Final[str] = """You are a prompt formatter and translator for an AI coding agent called OdIn.

Your job is to take the user's raw input and:
1. Fix spelling and grammar errors
2. Structure the prompt clearly with proper formatting
3. If the input is in any non-English language, translate it to English while preserving technical terms
4. Reframe vague requests into clear, actionable instructions
5. Preserve ALL original intent — do NOT add, remove, or change any requirements
6. If the prompt references files, paths, or code — keep them exactly as-is

Output ONLY the formatted prompt text. No explanations, no prefixes, no "Here is the formatted prompt:" —
just the clean formatted prompt ready to send to the agent.

Keep the original tone and urgency. If the user is brief, keep it brief but clear. If detailed, keep the detail."""


class GeminiFormatter:
    """Formats user prompts using Google Gemini API.

    API key lifecycle:
    - On toggle-ON, loads from tenant secrets via platform API
    - If not found, user is prompted; key is saved to tenant secrets
    - Key is validated against Gemini models endpoint before use
    - Auto-disables after each format_prompt() call
    """

    __slots__ = ("_api_key", "_enabled", "_key_validated")

    def __init__(self, api_key: str | None = None) -> None:
        """Initialize the formatter.

        Args:
            api_key: Optional API key. If not provided, loads from environment.

        """
        self._api_key = api_key or _settings.gemini_api_key
        self._enabled = False
        self._key_validated = False

    @property
    def enabled(self) -> bool:
        """Return whether the formatter is enabled and has an API key."""
        return self._enabled and bool(self._api_key)

    @enabled.setter
    def enabled(self, value: bool) -> None:
        """Enable or disable the formatter."""
        self._enabled = value

    @property
    def has_api_key(self) -> bool:
        """Return whether an API key is currently set."""
        return bool(self._api_key)

    @property
    def key_validated(self) -> bool:
        """Return whether the current API key has been validated."""
        return self._key_validated

    def set_api_key(self, key: str) -> None:
        """Set a new API key and reset validation status.

        Args:
            key: The new API key to set.

        """
        self._api_key = key
        self._key_validated = False

    def toggle(self) -> bool:
        """Toggle formatter on/off. Returns new state."""
        self._enabled = not self._enabled
        return self._enabled

    async def load_key_from_platform(
        self,
        tenant_id: str,
        platform_client: PlatformClient,
    ) -> bool:
        """Load Gemini API key from tenant secrets via api.aivibe.cloud.

        Args:
            tenant_id: Tenant identifier from JWT.
            platform_client: Authenticated platform client.

        Returns:
            True if a valid key was loaded, False if not found or invalid.

        """
        try:
            secret_value = await platform_client.get_tenant_secret(
                tenant_id=tenant_id,
                secret_name=TENANT_CREDENTIAL_NAME,
            )
            if not secret_value:
                logger.info("gemini_key_not_in_tenant_secrets", tenant_id=tenant_id)
                return False

            # Validate the stored key is still valid
            is_valid = await self.validate_api_key(secret_value)
            if not is_valid:
                logger.warning("gemini_key_from_secrets_invalid", tenant_id=tenant_id)
                return False

            self._api_key = secret_value
            self._key_validated = True
            logger.info("gemini_key_loaded_from_tenant_secrets", tenant_id=tenant_id)
        except Exception as exc:
            logger.warning(
                "gemini_key_platform_load_failed",
                tenant_id=tenant_id,
                error=str(exc)[:200],
            )
            return False
        else:
            return True

    async def save_key_to_platform(
        self,
        tenant_id: str,
        platform_client: PlatformClient,
        api_key: str,
    ) -> bool:
        """Save Gemini API key to tenant secrets via api.aivibe.cloud.

        The key is encrypted server-side (KMS) and stored in RDS with
        tenant-level RLS. Only this tenant can read it back.

        Args:
            tenant_id: Tenant identifier from JWT.
            platform_client: Authenticated platform client.
            api_key: Gemini API key to store.

        Returns:
            True if saved successfully, False on failure.

        """
        try:
            result = await platform_client.store_tenant_secret(
                tenant_id=tenant_id,
                secret_name=TENANT_CREDENTIAL_NAME,
                secret_value=api_key,
            )
            success = result.get("success", False)
            if success:
                self._api_key = api_key
                self._key_validated = True
                logger.info("gemini_key_saved_to_tenant_secrets", tenant_id=tenant_id)
            else:
                logger.warning(
                    "gemini_key_save_failed",
                    tenant_id=tenant_id,
                    error=result.get("error", "unknown"),
                )
        except Exception as exc:
            logger.warning(
                "gemini_key_platform_save_failed",
                tenant_id=tenant_id,
                error=str(exc)[:200],
            )
            return False
        else:
            return success

    async def validate_api_key(self, api_key: str | None = None) -> bool:
        """Validate a Gemini API key by calling the models list endpoint.

        Args:
            api_key: Key to validate. Uses stored key if None.

        Returns:
            True if the key is valid and returns a successful response.

        """
        key = api_key or self._api_key
        if not key:
            return False

        url = GEMINI_VALIDATE_URL.format(key=key)
        try:
            async with httpx.AsyncClient(timeout=10.0) as client:
                resp = await client.get(url)
                if resp.status_code == HTTP_OK:
                    self._key_validated = True
                    return True
                logger.warning(
                    "gemini_key_validation_failed",
                    status=resp.status_code,
                )
                return False
        except Exception as exc:
            logger.warning("gemini_key_validation_error", error=str(exc)[:HTTP_OK])
            return False

    async def format_prompt(self, user_input: str) -> str | None:
        """Format user prompt via Gemini API.

        Returns formatted prompt string, or None if formatting fails.
        Auto-disabling is handled by the caller (interactive loop).
        """
        if not self._api_key:
            logger.warning("gemini_api_key_not_set")
            return None

        url = f"{GEMINI_API_URL}?key={self._api_key}"

        payload = {
            "contents": [
                {
                    "parts": [
                        {"text": f"{FORMATTER_SYSTEM_PROMPT}\n\nUser prompt:\n{user_input}"},
                    ],
                },
            ],
            "generationConfig": {
                "temperature": 0.3,
                "maxOutputTokens": 4096,
            },
        }

        try:
            async with httpx.AsyncClient(timeout=30.0) as client:
                resp = await client.post(
                    url,
                    json=payload,
                    headers={"Content-Type": "application/json"},
                )
                resp.raise_for_status()
                data = resp.json()

                candidates = data.get("candidates", [])
                if candidates:
                    content = candidates[0].get("content", {})
                    parts = content.get("parts", [])
                    if parts:
                        return parts[0].get("text", "").strip()

                logger.warning("gemini_empty_response")
                return None

        except httpx.HTTPStatusError as e:
            if e.response.status_code in (401, 403):
                self._key_validated = False
                logger.warning("gemini_api_key_rejected", status=e.response.status_code)
            else:
                logger.warning(
                    "gemini_api_error",
                    status=e.response.status_code,
                    error=str(e)[:200],
                )
            return None
        except Exception as e:
            logger.warning("gemini_format_failed", error=str(e)[:200])
            return None
