"""
Monitoring recorder for orchestrator metrics.
[AC-AISVC-93~AC-AISVC-95] Records execution metrics for dashboard and testing.
"""

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

from sqlalchemy.ext.asyncio import AsyncSession

from app.models.entities import FlowTestRecord, FlowTestRecordStatus
from app.services.monitoring.cache import MonitoringCache

logger = logging.getLogger(__name__)


@dataclass
class StepMetrics:
    """Metrics for a single pipeline step."""

    step: int
    name: str
    status: str = "success"
    duration_ms: int = 0
    input_data: dict[str, Any] | None = None
    output_data: dict[str, Any] | None = None
    error: str | None = None
    step_metadata: dict[str, Any] = field(default_factory=dict)

    def to_dict(self) -> dict[str, Any]:
        result = {
            "step": self.step,
            "name": self.name,
            "status": self.status,
            "durationMs": self.duration_ms,
        }
        if self.input_data:
            result["input"] = self.input_data
        if self.output_data:
            result["output"] = self.output_data
        if self.error:
            result["error"] = self.error
        if self.step_metadata:
            result["metadata"] = self.step_metadata
        return result


class MonitoringRecorder:
    """
    [AC-AISVC-93~AC-AISVC-95] Records orchestrator execution metrics.

    Features:
    - Records step-by-step execution timing
    - Updates Redis counters for dashboard
    - Creates FlowTestRecord for testing mode
    """

    STEP_NAMES = {
        1: "InputScanner",
        2: "FlowEngine",
        3: "IntentRouter",
        4: "QueryRewriter",
        5: "MultiKBRetrieval",
        6: "ResultRanker",
        7: "PromptBuilder",
        8: "LLMGenerate",
        9: "OutputFilter",
        10: "Confidence",
        11: "Memory",
        12: "Response",
    }

    def __init__(
        self,
        tenant_id: str,
        session_id: str,
        cache: MonitoringCache | None = None,
        test_mode: bool = False,
        session: AsyncSession | None = None,
    ):
        self._tenant_id = tenant_id
        self._session_id = session_id
        self._cache = cache
        self._test_mode = test_mode
        self._session = session
        self._steps: list[StepMetrics] = []
        self._start_time = time.time()
        self._step_start_time: float | None = None
        self._current_step: int = 0

    def start_step(self, step: int) -> None:
        """Start timing a step."""
        self._current_step = step
        self._step_start_time = time.time()

    def end_step(
        self,
        status: str = "success",
        input_data: dict[str, Any] | None = None,
        output_data: dict[str, Any] | None = None,
        error: str | None = None,
        step_metadata: dict[str, Any] | None = None,
    ) -> StepMetrics:
        """End timing a step and record metrics."""
        duration_ms = int((time.time() - (self._step_start_time or time.time())) * 1000)

        step_metrics = StepMetrics(
            step=self._current_step,
            name=self.STEP_NAMES.get(self._current_step, f"Step{self._current_step}"),
            status=status,
            duration_ms=duration_ms,
            input_data=input_data,
            output_data=output_data,
            error=error,
            step_metadata=step_metadata or {},
        )

        self._steps.append(step_metrics)
        self._step_start_time = None

        return step_metrics

    async def record_intent_hit(
        self,
        rule_id: str,
        rule_name: str,
        response_type: str,
    ) -> None:
        """Record an intent rule hit."""
        if self._cache:
            await self._cache.incr_counter(self._tenant_id, "intent_hit")
            await self._cache.incr_leaderboard_member(
                self._tenant_id, "intent_rules", rule_id
            )

        logger.info(
            f"[MonitoringRecorder] Intent hit: rule={rule_name}, "
            f"type={response_type}, tenant={self._tenant_id}"
        )

    async def record_template_usage(
        self,
        template_id: str,
        template_name: str,
    ) -> None:
        """Record prompt template usage."""
        if self._cache:
            await self._cache.incr_counter(self._tenant_id, "template_use")
            await self._cache.incr_leaderboard_member(
                self._tenant_id, "templates", template_id
            )

        logger.info(
            f"[MonitoringRecorder] Template usage: template={template_name}, "
            f"tenant={self._tenant_id}"
        )

    async def record_flow_activation(
        self,
        flow_id: str,
        flow_name: str,
    ) -> None:
        """Record script flow activation."""
        if self._cache:
            await self._cache.incr_counter(self._tenant_id, "flow_activate")
            await self._cache.incr_leaderboard_member(
                self._tenant_id, "flows", flow_id
            )

        logger.info(
            f"[MonitoringRecorder] Flow activation: flow={flow_name}, "
            f"tenant={self._tenant_id}"
        )

    async def record_guardrail_block(
        self,
        word_id: str,
        word: str,
        category: str,
        strategy: str,
    ) -> None:
        """Record guardrail block."""
        if self._cache:
            await self._cache.incr_counter(self._tenant_id, "guardrail_block")
            await self._cache.incr_leaderboard_member(
                self._tenant_id, "guardrails", word_id
            )

        logger.info(
            f"[MonitoringRecorder] Guardrail block: word={word}, "
            f"category={category}, strategy={strategy}, tenant={self._tenant_id}"
        )

    async def save_test_record(
        self,
        final_response: dict[str, Any] | None = None,
    ) -> uuid.UUID | None:
        """Save FlowTestRecord if in test mode."""
        if not self._test_mode or not self._session:
            return None

        total_duration_ms = int((time.time() - self._start_time) * 1000)

        has_failure = any(s.status == "failed" for s in self._steps)
        has_partial = any(s.status == "skipped" for s in self._steps)

        if has_failure:
            status = FlowTestRecordStatus.FAILED.value
        elif has_partial:
            status = FlowTestRecordStatus.PARTIAL.value
        else:
            status = FlowTestRecordStatus.SUCCESS.value

        record = FlowTestRecord(
            tenant_id=self._tenant_id,
            session_id=self._session_id,
            status=status,
            steps=[s.to_dict() for s in self._steps],
            final_response=final_response,
            total_duration_ms=total_duration_ms,
        )

        self._session.add(record)
        await self._session.commit()
        await self._session.refresh(record)

        logger.info(
            f"[MonitoringRecorder] Saved test record: id={record.id}, "
            f"status={status}, duration={total_duration_ms}ms"
        )

        return record.id

    def get_all_steps(self) -> list[dict[str, Any]]:
        """Get all recorded steps as dicts."""
        return [s.to_dict() for s in self._steps]

    def get_total_duration_ms(self) -> int:
        """Get total execution duration in milliseconds."""
        return int((time.time() - self._start_time) * 1000)
