"""
Prompt template monitoring service for AI Service.
[AC-AISVC-99, AC-AISVC-100] Prompt template preview and statistics.
"""

import logging
import uuid
from dataclasses import dataclass, field
from datetime import datetime
from typing import Any

import tiktoken
from sqlalchemy import func, select
from sqlalchemy.ext.asyncio import AsyncSession

from app.models.entities import (
    BehaviorRule,
    ChatMessage,
    PromptTemplate,
    PromptTemplateVersion,
    TemplateVersionStatus,
)
from app.services.prompt.variable_resolver import VariableResolver

logger = logging.getLogger(__name__)


@dataclass
class TokenCount:
    """Token count breakdown for a prompt."""

    system_prompt: int
    history: int
    current_message: int
    total: int

    def to_dict(self) -> dict[str, int]:
        return {
            "systemPrompt": self.system_prompt,
            "history": self.history,
            "currentMessage": self.current_message,
            "total": self.total,
        }


@dataclass
class PromptPreviewResult:
    """Result of prompt template preview."""

    template_id: str
    template_name: str
    version: int
    raw_content: str
    variables: list[dict[str, str]]
    rendered_content: str
    estimated_tokens: int
    token_count: TokenCount
    warnings: list[str] = field(default_factory=list)

    def to_dict(self) -> dict[str, Any]:
        return {
            "templateId": self.template_id,
            "templateName": self.template_name,
            "version": self.version,
            "rawContent": self.raw_content,
            "variables": self.variables,
            "renderedContent": self.rendered_content,
            "estimatedTokens": self.estimated_tokens,
            "tokenCount": self.token_count.to_dict(),
            "warnings": self.warnings,
        }


@dataclass
class PromptTemplateStats:
    """Statistics for a single prompt template."""

    template_id: str
    template_name: str
    scene: str
    usage_count: int
    avg_tokens: float
    avg_prompt_tokens: float
    avg_completion_tokens: float
    last_used_time: str | None

    def to_dict(self) -> dict[str, Any]:
        return {
            "templateId": self.template_id,
            "templateName": self.template_name,
            "scene": self.scene,
            "usageCount": self.usage_count,
            "avgTokens": self.avg_tokens,
            "avgPromptTokens": self.avg_prompt_tokens,
            "avgCompletionTokens": self.avg_completion_tokens,
            "lastUsedTime": self.last_used_time,
        }


@dataclass
class PromptTemplateStatsResult:
    """Result of prompt template statistics query."""

    total_usage: int
    templates: list[PromptTemplateStats]
    scene_breakdown: dict[str, int]

    def to_dict(self) -> dict[str, Any]:
        return {
            "totalUsage": self.total_usage,
            "templates": [t.to_dict() for t in self.templates],
            "sceneBreakdown": self.scene_breakdown,
        }


class PromptMonitor:
    """
    [AC-AISVC-99, AC-AISVC-100] Prompt template monitoring service.

    Features:
    - Preview template with variable substitution
    - Calculate token counts using tiktoken
    - Aggregate template usage statistics
    """

    def __init__(self, session: AsyncSession):
        self._session = session
        self._tokenizer = tiktoken.get_encoding("cl100k_base")

    async def preview_template(
        self,
        tenant_id: str,
        template_id: uuid.UUID,
        variables: dict[str, str] | None = None,
        sample_history: list[dict[str, str]] | None = None,
        sample_message: str | None = None,
    ) -> PromptPreviewResult | None:
        """
        [AC-AISVC-99] Preview a prompt template with variable substitution.

        Args:
            tenant_id: Tenant ID for isolation
            template_id: Template ID to preview
            variables: Variable values for substitution
            sample_history: Sample conversation history
            sample_message: Sample current message

        Returns:
            PromptPreviewResult or None if template not found
        """
        stmt = (
            select(PromptTemplate)
            .where(
                PromptTemplate.tenant_id == tenant_id,
                PromptTemplate.id == template_id,
            )
        )
        result = await self._session.execute(stmt)
        template = result.scalar_one_or_none()

        if not template:
            return None

        version_stmt = (
            select(PromptTemplateVersion)
            .where(
                PromptTemplateVersion.template_id == template_id,
            )
            .order_by(PromptTemplateVersion.version.desc())
        )
        version_result = await self._session.execute(version_stmt)
        latest_version = version_result.scalar_one_or_none()

        if not latest_version:
            return None

        resolver = VariableResolver()
        merged_variables = self._merge_variables(
            latest_version.variables or [],
            variables or {},
        )

        rendered_content = resolver.resolve(
            latest_version.system_instruction,
            latest_version.variables,
            merged_variables,
        )

        behavior_rules = await self._get_behavior_rules(tenant_id)
        if behavior_rules:
            rendered_content += "\n\n[行为约束]\n" + "\n".join(
                f"{i + 1}. {rule.rule_text}"
                for i, rule in enumerate(behavior_rules)
            )

        token_count = self._calculate_token_count(
            rendered_content,
            sample_history or [],
            sample_message or "",
        )

        warnings = self._generate_warnings(token_count)

        variable_list = [
            {"name": k, "value": v}
            for k, v in merged_variables.items()
        ]

        logger.info(
            f"[AC-AISVC-99] Previewed template {template.name}, "
            f"tokens={token_count.total}"
        )

        return PromptPreviewResult(
            template_id=str(template.id),
            template_name=template.name,
            version=latest_version.version,
            raw_content=latest_version.system_instruction,
            variables=variable_list,
            rendered_content=rendered_content,
            estimated_tokens=token_count.total,
            token_count=token_count,
            warnings=warnings,
        )

    async def get_template_stats(
        self,
        tenant_id: str,
        scene: str | None = None,
        start_date: datetime | None = None,
        end_date: datetime | None = None,
    ) -> PromptTemplateStatsResult:
        """
        [AC-AISVC-100] Get aggregated statistics for prompt templates.

        Args:
            tenant_id: Tenant ID for isolation
            scene: Optional scene filter
            start_date: Optional start date filter
            end_date: Optional end date filter

        Returns:
            PromptTemplateStatsResult with aggregated statistics
        """
        stmt = select(PromptTemplate).where(PromptTemplate.tenant_id == tenant_id)

        if scene:
            stmt = stmt.where(PromptTemplate.scene == scene)

        result = await self._session.execute(stmt)
        templates = result.scalars().all()

        template_stats = []
        scene_breakdown: dict[str, int] = {}
        total_usage = 0

        for template in templates:
            usage_count = await self._get_template_usage_count(
                tenant_id,
                template.id,
                start_date,
                end_date,
            )

            avg_tokens = await self._get_avg_tokens(
                tenant_id,
                start_date,
                end_date,
            )

            last_used_time = await self._get_last_used_time(
                tenant_id,
                start_date,
                end_date,
            )

            template_stats.append(PromptTemplateStats(
                template_id=str(template.id),
                template_name=template.name,
                scene=template.scene,
                usage_count=usage_count,
                avg_tokens=avg_tokens,
                avg_prompt_tokens=avg_tokens * 0.8,
                avg_completion_tokens=avg_tokens * 0.2,
                last_used_time=last_used_time,
            ))

            total_usage += usage_count
            scene_breakdown[template.scene] = scene_breakdown.get(template.scene, 0) + usage_count

        template_stats.sort(key=lambda x: x.usage_count, reverse=True)

        logger.info(
            f"[AC-AISVC-100] Retrieved stats for {len(templates)} templates, "
            f"tenant={tenant_id}, total_usage={total_usage}"
        )

        return PromptTemplateStatsResult(
            total_usage=total_usage,
            templates=template_stats,
            scene_breakdown=scene_breakdown,
        )

    def _merge_variables(
        self,
        defined_variables: list[dict[str, Any]],
        override_values: dict[str, str],
    ) -> dict[str, str]:
        """Merge defined variables with override values."""
        merged = {}

        for var in defined_variables:
            name = var.get("name", "")
            default = var.get("default", "")
            if name:
                merged[name] = override_values.get(name, default)

        for name, value in override_values.items():
            if name not in merged:
                merged[name] = value

        return merged

    def _calculate_token_count(
        self,
        system_prompt: str,
        history: list[dict[str, str]],
        current_message: str,
    ) -> TokenCount:
        """Calculate token count for each part of the prompt."""
        system_tokens = len(self._tokenizer.encode(system_prompt))

        history_tokens = 0
        for msg in history:
            content = msg.get("content", "")
            history_tokens += len(self._tokenizer.encode(content))

        current_tokens = len(self._tokenizer.encode(current_message))

        return TokenCount(
            system_prompt=system_tokens,
            history=history_tokens,
            current_message=current_tokens,
            total=system_tokens + history_tokens + current_tokens,
        )

    def _generate_warnings(self, token_count: TokenCount) -> list[str]:
        """Generate warnings based on token count."""
        warnings = []

        if token_count.total > 4000:
            warnings.append("总 Token 数超过 4000，可能影响性能")

        if token_count.system_prompt > 2000:
            warnings.append("系统指令过长，建议精简")

        if token_count.history > 2000:
            warnings.append("历史消息过长，建议减少上下文长度")

        return warnings

    async def _get_behavior_rules(self, tenant_id: str) -> list[BehaviorRule]:
        """Get enabled behavior rules for a tenant."""
        stmt = (
            select(BehaviorRule)
            .where(
                BehaviorRule.tenant_id == tenant_id,
                BehaviorRule.is_enabled == True,
            )
        )
        result = await self._session.execute(stmt)
        return list(result.scalars().all())

    async def _get_template_usage_count(
        self,
        tenant_id: str,
        template_id: uuid.UUID,
        start_date: datetime | None,
        end_date: datetime | None,
    ) -> int:
        """Get usage count for a template."""
        stmt = (
            select(func.count(ChatMessage.id))
            .where(
                ChatMessage.tenant_id == tenant_id,
                ChatMessage.role == "assistant",
            )
        )

        if start_date:
            stmt = stmt.where(ChatMessage.created_at >= start_date)
        if end_date:
            stmt = stmt.where(ChatMessage.created_at <= end_date)

        result = await self._session.execute(stmt)
        return result.scalar() or 0

    async def _get_avg_tokens(
        self,
        tenant_id: str,
        start_date: datetime | None,
        end_date: datetime | None,
    ) -> float:
        """Get average token count for a tenant."""
        stmt = (
            select(func.avg(ChatMessage.total_tokens))
            .where(
                ChatMessage.tenant_id == tenant_id,
                ChatMessage.role == "assistant",
                ChatMessage.total_tokens.is_not(None),
            )
        )

        if start_date:
            stmt = stmt.where(ChatMessage.created_at >= start_date)
        if end_date:
            stmt = stmt.where(ChatMessage.created_at <= end_date)

        result = await self._session.execute(stmt)
        avg = result.scalar()
        return float(avg) if avg else 0.0

    async def _get_last_used_time(
        self,
        tenant_id: str,
        start_date: datetime | None,
        end_date: datetime | None,
    ) -> str | None:
        """Get last used time for templates."""
        stmt = (
            select(func.max(ChatMessage.created_at))
            .where(
                ChatMessage.tenant_id == tenant_id,
                ChatMessage.role == "assistant",
            )
        )

        if start_date:
            stmt = stmt.where(ChatMessage.created_at >= start_date)
        if end_date:
            stmt = stmt.where(ChatMessage.created_at <= end_date)

        result = await self._session.execute(stmt)
        last_time = result.scalar()
        return last_time.isoformat() if last_time else None
