"""
Output filter for AI Service.
[AC-AISVC-82] LLM output post-filtering with mask/replace/block strategies.
"""

import logging
from typing import Any

from app.models.entities import (
    ForbiddenWord,
    ForbiddenWordStrategy,
    GuardrailResult,
)
from app.services.guardrail.word_service import ForbiddenWordService

logger = logging.getLogger(__name__)


class OutputFilter:
    """
    [AC-AISVC-82] Output filter for post-filtering LLM responses.

    Features:
    - Scans LLM output for forbidden words
    - Applies mask/replace/block strategies
    - Returns fallback reply for block strategy
    - Records triggered words in metadata
    """

    DEFAULT_FALLBACK_REPLY = "抱歉，让我换个方式回答您"

    def __init__(self, word_service: ForbiddenWordService):
        self._word_service = word_service

    async def filter(
        self,
        reply: str,
        tenant_id: str,
    ) -> GuardrailResult:
        """
        [AC-AISVC-82] Filter LLM output for forbidden words.

        Args:
            reply: LLM generated reply to filter
            tenant_id: Tenant ID for isolation

        Returns:
            GuardrailResult with filtered reply and trigger info
        """
        if not reply or not reply.strip():
            return GuardrailResult(reply=reply)

        words = await self._word_service.get_enabled_words_for_filtering(tenant_id)

        if not words:
            return GuardrailResult(reply=reply)

        triggered_words: list[str] = []
        triggered_categories: list[str] = []
        filtered_reply = reply
        blocked = False
        fallback_reply = self.DEFAULT_FALLBACK_REPLY

        for word in words:
            if word.word in filtered_reply:
                triggered_words.append(word.word)
                if word.category not in triggered_categories:
                    triggered_categories.append(word.category)

                if word.strategy == ForbiddenWordStrategy.BLOCK.value:
                    blocked = True
                    fallback_reply = word.fallback_reply or self.DEFAULT_FALLBACK_REPLY
                    logger.warning(
                        f"[AC-AISVC-82] Output blocked by forbidden word: tenant={tenant_id}, "
                        f"word={word.word}, category={word.category}"
                    )
                    break

                elif word.strategy == ForbiddenWordStrategy.MASK.value:
                    filtered_reply = filtered_reply.replace(word.word, "*" * len(word.word))
                    logger.info(
                        f"[AC-AISVC-82] Output masked: tenant={tenant_id}, word={word.word}"
                    )

                elif word.strategy == ForbiddenWordStrategy.REPLACE.value:
                    replacement = word.replacement or ""
                    filtered_reply = filtered_reply.replace(word.word, replacement)
                    logger.info(
                        f"[AC-AISVC-82] Output replaced: tenant={tenant_id}, "
                        f"word={word.word} -> {replacement}"
                    )

        if blocked:
            return GuardrailResult(
                reply=fallback_reply,
                blocked=True,
                triggered_words=triggered_words,
                triggered_categories=triggered_categories,
            )

        if triggered_words:
            logger.info(
                f"[AC-AISVC-82] Output filtered: tenant={tenant_id}, "
                f"triggered_words={triggered_words}, categories={triggered_categories}"
            )

            for word_entity in self._get_triggered_word_entities(words, triggered_words):
                try:
                    await self._word_service.increment_hit_count(tenant_id, word_entity.id)
                except Exception as e:
                    logger.warning(
                        f"Failed to increment hit count for word {word_entity.id}: {e}"
                    )

        return GuardrailResult(
            reply=filtered_reply,
            blocked=False,
            triggered_words=triggered_words,
            triggered_categories=triggered_categories,
        )

    def _get_triggered_word_entities(
        self,
        words: list[ForbiddenWord],
        triggered_words: list[str],
    ) -> list[ForbiddenWord]:
        """Get word entities for triggered words."""
        return [w for w in words if w.word in triggered_words]

    async def filter_and_enrich_metadata(
        self,
        reply: str,
        tenant_id: str,
        metadata: dict[str, Any] | None = None,
    ) -> tuple[str, dict[str, Any]]:
        """
        [AC-AISVC-82] Filter output and enrich metadata with filter result.

        Args:
            reply: LLM generated reply to filter
            tenant_id: Tenant ID for isolation
            metadata: Existing metadata dict to enrich

        Returns:
            Tuple of (filtered_reply, enriched_metadata)
        """
        result = await self.filter(reply, tenant_id)

        if metadata is None:
            metadata = {}

        metadata.update(result.to_dict())

        return result.reply, metadata
