"""
Intent rule service for AI Service.
[AC-AISVC-65~AC-AISVC-68] Intent rule CRUD and hit statistics management.
"""

import logging
import time
import uuid
from collections.abc import Sequence
from datetime import datetime
from typing import Any

from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from sqlmodel import col

from app.models.entities import (
    IntentRule,
    IntentRuleCreate,
    IntentRuleUpdate,
)

logger = logging.getLogger(__name__)

RULE_CACHE_TTL_SECONDS = 60


class RuleCache:
    """
    [AC-AISVC-69] In-memory cache for intent rules.
    Key: tenant_id
    Value: (rules_list, cached_at)
    TTL: 60 seconds
    """

    def __init__(self, ttl_seconds: int = RULE_CACHE_TTL_SECONDS):
        self._cache: dict[str, tuple[list[IntentRule], float]] = {}
        self._ttl = ttl_seconds

    def get(self, tenant_id: str) -> list[IntentRule] | None:
        """Get cached rules if not expired."""
        if tenant_id in self._cache:
            rules, cached_at = self._cache[tenant_id]
            if time.time() - cached_at < self._ttl:
                return rules
            else:
                del self._cache[tenant_id]
        return None

    def set(self, tenant_id: str, rules: list[IntentRule]) -> None:
        """Cache rules for a tenant."""
        self._cache[tenant_id] = (rules, time.time())

    def invalidate(self, tenant_id: str) -> None:
        """Invalidate cache for a tenant."""
        if tenant_id in self._cache:
            del self._cache[tenant_id]
            logger.debug(f"Invalidated rule cache for tenant={tenant_id}")


_rule_cache = RuleCache()


class IntentRuleService:
    """
    [AC-AISVC-65~AC-AISVC-68] Service for managing intent rules.

    Features:
    - Rule CRUD with tenant isolation
    - Hit count statistics
    - In-memory caching with TTL
    - Cache invalidation on CRUD operations
    """

    def __init__(self, session: AsyncSession):
        self._session = session
        self._cache = _rule_cache

    async def create_rule(
        self,
        tenant_id: str,
        create_data: IntentRuleCreate,
    ) -> IntentRule:
        """
        [AC-AISVC-65] Create a new intent rule.
        [AC-IDSMETA-16] Support metadata field.
        """
        flow_id_uuid = None
        if create_data.flow_id:
            try:
                flow_id_uuid = uuid.UUID(create_data.flow_id)
            except ValueError:
                pass

        rule = IntentRule(
            tenant_id=tenant_id,
            name=create_data.name,
            keywords=create_data.keywords or [],
            patterns=create_data.patterns or [],
            priority=create_data.priority,
            response_type=create_data.response_type,
            target_kb_ids=create_data.target_kb_ids or [],
            flow_id=flow_id_uuid,
            fixed_reply=create_data.fixed_reply,
            transfer_message=create_data.transfer_message,
            is_enabled=True,
            hit_count=0,
            metadata_=create_data.metadata_,
            intent_vector=create_data.intent_vector,
            semantic_examples=create_data.semantic_examples,
        )
        self._session.add(rule)
        await self._session.flush()

        self._cache.invalidate(tenant_id)

        logger.info(
            f"[AC-AISVC-65][AC-IDSMETA-16] Created intent rule: tenant={tenant_id}, "
            f"id={rule.id}, name={rule.name}, response_type={rule.response_type}"
        )
        return rule

    async def list_rules(
        self,
        tenant_id: str,
        response_type: str | None = None,
        is_enabled: bool | None = None,
    ) -> Sequence[IntentRule]:
        """
        [AC-AISVC-66] List rules for a tenant with optional filters.
        """
        stmt = select(IntentRule).where(
            IntentRule.tenant_id == tenant_id
        )

        if response_type is not None:
            stmt = stmt.where(IntentRule.response_type == response_type)

        if is_enabled is not None:
            stmt = stmt.where(IntentRule.is_enabled == is_enabled)

        stmt = stmt.order_by(col(IntentRule.priority).desc(), col(IntentRule.created_at).desc())
        result = await self._session.execute(stmt)
        return result.scalars().all()

    async def get_rule(
        self,
        tenant_id: str,
        rule_id: uuid.UUID,
    ) -> IntentRule | None:
        """
        [AC-AISVC-66] Get rule by ID with tenant isolation.
        """
        stmt = select(IntentRule).where(
            IntentRule.tenant_id == tenant_id,
            IntentRule.id == rule_id,
        )
        result = await self._session.execute(stmt)
        return result.scalar_one_or_none()

    async def update_rule(
        self,
        tenant_id: str,
        rule_id: uuid.UUID,
        update_data: IntentRuleUpdate,
    ) -> IntentRule | None:
        """
        [AC-AISVC-67] Update an intent rule.
        [AC-IDSMETA-16] Support metadata field.
        """
        rule = await self.get_rule(tenant_id, rule_id)
        if not rule:
            return None

        if update_data.name is not None:
            rule.name = update_data.name
        if update_data.keywords is not None:
            rule.keywords = update_data.keywords
        if update_data.patterns is not None:
            rule.patterns = update_data.patterns
        if update_data.priority is not None:
            rule.priority = update_data.priority
        if update_data.response_type is not None:
            rule.response_type = update_data.response_type
        if update_data.target_kb_ids is not None:
            rule.target_kb_ids = update_data.target_kb_ids
        if update_data.flow_id is not None:
            try:
                rule.flow_id = uuid.UUID(update_data.flow_id)
            except ValueError:
                rule.flow_id = None
        if update_data.fixed_reply is not None:
            rule.fixed_reply = update_data.fixed_reply
        if update_data.transfer_message is not None:
            rule.transfer_message = update_data.transfer_message
        if update_data.is_enabled is not None:
            rule.is_enabled = update_data.is_enabled
        if update_data.metadata_ is not None:
            rule.metadata_ = update_data.metadata_
        if update_data.intent_vector is not None:
            rule.intent_vector = update_data.intent_vector
        if update_data.semantic_examples is not None:
            rule.semantic_examples = update_data.semantic_examples

        rule.updated_at = datetime.utcnow()
        await self._session.flush()

        self._cache.invalidate(tenant_id)

        logger.info(
            f"[AC-AISVC-67][AC-IDSMETA-16] Updated intent rule: tenant={tenant_id}, id={rule_id}"
        )
        return rule

    async def delete_rule(
        self,
        tenant_id: str,
        rule_id: uuid.UUID,
    ) -> bool:
        """
        [AC-AISVC-68] Delete an intent rule.
        """
        rule = await self.get_rule(tenant_id, rule_id)
        if not rule:
            return False

        await self._session.delete(rule)
        await self._session.flush()

        self._cache.invalidate(tenant_id)

        logger.info(
            f"[AC-AISVC-68] Deleted intent rule: tenant={tenant_id}, id={rule_id}"
        )
        return True

    async def increment_hit_count(
        self,
        tenant_id: str,
        rule_id: uuid.UUID,
    ) -> bool:
        """
        [AC-AISVC-66] Increment hit count for a rule.
        """
        rule = await self.get_rule(tenant_id, rule_id)
        if not rule:
            return False

        rule.hit_count += 1
        rule.updated_at = datetime.utcnow()
        await self._session.flush()

        logger.debug(
            f"[AC-AISVC-66] Incremented hit count for rule: tenant={tenant_id}, "
            f"id={rule_id}, hit_count={rule.hit_count}"
        )
        return True

    async def get_enabled_rules_for_matching(
        self,
        tenant_id: str,
    ) -> list[IntentRule]:
        """
        [AC-AISVC-69] Get enabled rules for matching, ordered by priority DESC.
        Uses cache for performance.
        """
        cached = self._cache.get(tenant_id)
        if cached is not None:
            logger.debug(f"[AC-AISVC-69] Cache hit for rules: tenant={tenant_id}")
            return cached

        stmt = (
            select(IntentRule)
            .where(
                IntentRule.tenant_id == tenant_id,
                IntentRule.is_enabled == True,  # noqa: E712
            )
            .order_by(col(IntentRule.priority).desc())
        )
        result = await self._session.execute(stmt)
        rules = list(result.scalars().all())

        self._cache.set(tenant_id, rules)
        logger.info(
            f"[AC-AISVC-69] Loaded {len(rules)} enabled rules from DB: tenant={tenant_id}"
        )
        return rules

    def invalidate_cache(self, tenant_id: str) -> None:
        """Manually invalidate cache for a tenant."""
        self._cache.invalidate(tenant_id)

    async def rule_to_info_dict(self, rule: IntentRule) -> dict[str, Any]:
        """Convert rule entity to API response dict."""
        return {
            "id": str(rule.id),
            "name": rule.name,
            "keywords": rule.keywords or [],
            "patterns": rule.patterns or [],
            "priority": rule.priority,
            "response_type": rule.response_type,
            "target_kb_ids": rule.target_kb_ids or [],
            "flow_id": str(rule.flow_id) if rule.flow_id else None,
            "fixed_reply": rule.fixed_reply,
            "transfer_message": rule.transfer_message,
            "is_enabled": rule.is_enabled,
            "hit_count": rule.hit_count,
            "metadata": rule.metadata_,
            "intent_vector": rule.intent_vector,
            "semantic_examples": rule.semantic_examples,
            "created_at": rule.created_at.isoformat() if rule.created_at else None,
            "updated_at": rule.updated_at.isoformat() if rule.updated_at else None,
        }
