"""
Flow monitoring service for AI Service.
[AC-AISVC-103, AC-AISVC-104] Flow statistics and execution records.
"""

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

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

from app.models.entities import FlowInstance, FlowInstanceStatus, ScriptFlow

logger = logging.getLogger(__name__)


@dataclass
class DropOffPoint:
    """Drop-off point analysis for a flow step."""

    stepNo: int
    dropOffCount: int
    dropOffRate: float

    def to_dict(self) -> dict[str, Any]:
        return {
            "stepNo": self.stepNo,
            "dropOffCount": self.dropOffCount,
            "dropOffRate": round(self.dropOffRate, 2),
        }


@dataclass
class FlowStats:
    """Statistics for a single flow."""

    flowId: str
    flowName: str
    activationCount: int
    completionCount: int
    completionRate: float
    avgDuration: float
    avgStepsCompleted: float
    dropOffPoints: list[DropOffPoint] = field(default_factory=list)
    lastActivatedAt: str | None = None

    def to_dict(self) -> dict[str, Any]:
        return {
            "flowId": self.flowId,
            "flowName": self.flowName,
            "activationCount": self.activationCount,
            "completionCount": self.completionCount,
            "completionRate": round(self.completionRate, 2),
            "avgDuration": round(self.avgDuration, 1),
            "avgStepsCompleted": round(self.avgStepsCompleted, 1),
            "dropOffPoints": [d.to_dict() for d in self.dropOffPoints],
            "lastActivatedAt": self.lastActivatedAt,
        }


@dataclass
class FlowExecutionRecord:
    """A single flow execution record."""

    instanceId: str
    sessionId: str
    flowId: str
    flowName: str
    currentStep: int
    totalSteps: int
    status: str
    startedAt: str
    updatedAt: str
    completedAt: str | None
    context: dict[str, Any] | None = None

    def to_dict(self) -> dict[str, Any]:
        return {
            "instanceId": self.instanceId,
            "sessionId": self.sessionId,
            "flowId": self.flowId,
            "flowName": self.flowName,
            "currentStep": self.currentStep,
            "totalSteps": self.totalSteps,
            "status": self.status,
            "startedAt": self.startedAt,
            "updatedAt": self.updatedAt,
            "completedAt": self.completedAt,
        }


@dataclass
class FlowStatsResult:
    """Result of flow statistics query."""

    totalActivations: int
    totalCompletions: int
    completionRate: float
    flows: list[FlowStats]

    def to_dict(self) -> dict[str, Any]:
        return {
            "totalActivations": self.totalActivations,
            "totalCompletions": self.totalCompletions,
            "completionRate": round(self.completionRate, 2),
            "flows": [f.to_dict() for f in self.flows],
        }


class FlowMonitor:
    """
    [AC-AISVC-103, AC-AISVC-104] Flow monitoring service.

    Features:
    - Aggregate flow activation statistics
    - Calculate completion rates and average durations
    - Analyze drop-off points
    - Query flow execution records
    """

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

    async def get_flow_stats(
        self,
        tenant_id: str,
        start_date: datetime | None = None,
        end_date: datetime | None = None,
    ) -> FlowStatsResult:
        """
        [AC-AISVC-103] Get aggregated statistics for all flows.

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

        Returns:
            FlowStatsResult with aggregated statistics
        """
        flow_stmt = select(ScriptFlow).where(ScriptFlow.tenant_id == tenant_id)
        flow_result = await self._session.execute(flow_stmt)
        flows = flow_result.scalars().all()

        flow_stats_list: list[FlowStats] = []
        total_activations = 0
        total_completions = 0

        for flow in flows:
            stats = await self._get_single_flow_stats(
                tenant_id=tenant_id,
                flow=flow,
                start_date=start_date,
                end_date=end_date,
            )
            flow_stats_list.append(stats)
            total_activations += stats.activationCount
            total_completions += stats.completionCount

        flow_stats_list.sort(key=lambda x: x.activationCount, reverse=True)

        completion_rate = (
            total_completions / total_activations if total_activations > 0 else 0.0
        )

        logger.info(
            f"[AC-AISVC-103] Retrieved stats for {len(flows)} flows, "
            f"tenant={tenant_id}, total_activations={total_activations}"
        )

        return FlowStatsResult(
            totalActivations=total_activations,
            totalCompletions=total_completions,
            completionRate=completion_rate,
            flows=flow_stats_list,
        )

    async def _get_single_flow_stats(
        self,
        tenant_id: str,
        flow: ScriptFlow,
        start_date: datetime | None,
        end_date: datetime | None,
    ) -> FlowStats:
        """Get statistics for a single flow."""
        base_stmt = select(FlowInstance).where(
            FlowInstance.tenant_id == tenant_id,
            FlowInstance.flow_id == flow.id,
        )

        if start_date:
            base_stmt = base_stmt.where(FlowInstance.started_at >= start_date)
        if end_date:
            base_stmt = base_stmt.where(FlowInstance.started_at <= end_date)

        count_stmt = select(func.count(FlowInstance.id)).select_from(base_stmt.subquery())
        activation_result = await self._session.execute(count_stmt)
        activation_count = activation_result.scalar() or 0

        completion_stmt = select(func.count(FlowInstance.id)).select_from(
            base_stmt.where(
                FlowInstance.status == FlowInstanceStatus.COMPLETED.value
            ).subquery()
        )
        completion_result = await self._session.execute(completion_stmt)
        completion_count = completion_result.scalar() or 0

        completion_rate = completion_count / activation_count if activation_count > 0 else 0.0

        avg_duration = await self._get_avg_duration(flow.id, start_date, end_date)

        avg_steps = await self._get_avg_steps_completed(flow.id, start_date, end_date)

        drop_off_points = await self._analyze_drop_off_points(
            tenant_id, flow, start_date, end_date
        )

        last_activated_stmt = (
            select(FlowInstance.started_at)
            .where(FlowInstance.flow_id == flow.id)
            .order_by(col(FlowInstance.started_at).desc())
            .limit(1)
        )
        last_activated_result = await self._session.execute(last_activated_stmt)
        last_activated = last_activated_result.scalar_one_or_none()

        return FlowStats(
            flowId=str(flow.id),
            flowName=flow.name,
            activationCount=activation_count,
            completionCount=completion_count,
            completionRate=completion_rate,
            avgDuration=avg_duration,
            avgStepsCompleted=avg_steps,
            dropOffPoints=drop_off_points,
            lastActivatedAt=last_activated.isoformat() if last_activated else None,
        )

    async def _get_avg_duration(
        self,
        flow_id: uuid.UUID,
        start_date: datetime | None,
        end_date: datetime | None,
    ) -> float:
        """Get average completion duration in seconds."""
        stmt = (
            select(
                func.avg(
                    func.extract("epoch", FlowInstance.completed_at) -
                    func.extract("epoch", FlowInstance.started_at)
                )
            )
            .where(
                FlowInstance.flow_id == flow_id,
                FlowInstance.status == FlowInstanceStatus.COMPLETED.value,
                FlowInstance.completed_at.is_not(None),
            )
        )

        if start_date:
            stmt = stmt.where(FlowInstance.started_at >= start_date)
        if end_date:
            stmt = stmt.where(FlowInstance.started_at <= end_date)

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

    async def _get_avg_steps_completed(
        self,
        flow_id: uuid.UUID,
        start_date: datetime | None,
        end_date: datetime | None,
    ) -> float:
        """Get average steps completed."""
        stmt = (
            select(func.avg(FlowInstance.current_step))
            .where(FlowInstance.flow_id == flow_id)
        )

        if start_date:
            stmt = stmt.where(FlowInstance.started_at >= start_date)
        if end_date:
            stmt = stmt.where(FlowInstance.started_at <= end_date)

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

    async def _analyze_drop_off_points(
        self,
        tenant_id: str,
        flow: ScriptFlow,
        start_date: datetime | None,
        end_date: datetime | None,
    ) -> list[DropOffPoint]:
        """Analyze drop-off points for a flow."""
        total_steps = len(flow.steps)
        if total_steps == 0:
            return []

        drop_off_points: list[DropOffPoint] = []

        base_stmt = select(FlowInstance).where(
            FlowInstance.tenant_id == tenant_id,
            FlowInstance.flow_id == flow.id,
            FlowInstance.status != FlowInstanceStatus.COMPLETED.value,
        )

        if start_date:
            base_stmt = base_stmt.where(FlowInstance.started_at >= start_date)
        if end_date:
            base_stmt = base_stmt.where(FlowInstance.started_at <= end_date)

        total_instances_stmt = select(func.count(FlowInstance.id)).select_from(
            select(FlowInstance)
            .where(
                FlowInstance.tenant_id == tenant_id,
                FlowInstance.flow_id == flow.id,
            )
            .subquery()
        )
        if start_date:
            total_instances_stmt = total_instances_stmt.where(
                FlowInstance.started_at >= start_date
            )
        if end_date:
            total_instances_stmt = total_instances_stmt.where(
                FlowInstance.started_at <= end_date
            )

        total_result = await self._session.execute(
            select(func.count(FlowInstance.id)).where(
                FlowInstance.tenant_id == tenant_id,
                FlowInstance.flow_id == flow.id,
            )
        )
        total_instances = total_result.scalar() or 0

        if total_instances == 0:
            return []

        for step_no in range(1, total_steps + 1):
            drop_off_stmt = select(func.count(FlowInstance.id)).where(
                FlowInstance.tenant_id == tenant_id,
                FlowInstance.flow_id == flow.id,
                FlowInstance.current_step == step_no,
                FlowInstance.status != FlowInstanceStatus.COMPLETED.value,
            )

            if start_date:
                drop_off_stmt = drop_off_stmt.where(FlowInstance.started_at >= start_date)
            if end_date:
                drop_off_stmt = drop_off_stmt.where(FlowInstance.started_at <= end_date)

            drop_off_result = await self._session.execute(drop_off_stmt)
            drop_off_count = drop_off_result.scalar() or 0

            if drop_off_count > 0:
                drop_off_rate = drop_off_count / total_instances
                drop_off_points.append(
                    DropOffPoint(
                        stepNo=step_no,
                        dropOffCount=drop_off_count,
                        dropOffRate=drop_off_rate,
                    )
                )

        drop_off_points.sort(key=lambda x: x.dropOffCount, reverse=True)

        return drop_off_points[:5]

    async def get_flow_executions(
        self,
        tenant_id: str,
        flow_id: uuid.UUID,
        page: int = 1,
        page_size: int = 20,
    ) -> tuple[list[FlowExecutionRecord], int]:
        """
        [AC-AISVC-104] Get execution records for a specific flow.

        Args:
            tenant_id: Tenant ID for isolation
            flow_id: Flow ID to query
            page: Page number (1-indexed)
            page_size: Number of records per page

        Returns:
            Tuple of (execution_records, total_count)
        """
        flow = await self._get_flow(flow_id)
        flow_name = flow.name if flow else "Unknown"
        total_steps = len(flow.steps) if flow else 0

        offset = (page - 1) * page_size

        count_stmt = select(func.count(FlowInstance.id)).where(
            FlowInstance.tenant_id == tenant_id,
            FlowInstance.flow_id == flow_id,
        )
        count_result = await self._session.execute(count_stmt)
        total_count = count_result.scalar() or 0

        stmt = (
            select(FlowInstance)
            .where(
                FlowInstance.tenant_id == tenant_id,
                FlowInstance.flow_id == flow_id,
            )
            .order_by(col(FlowInstance.started_at).desc())
            .offset(offset)
            .limit(page_size)
        )
        result = await self._session.execute(stmt)
        instances = result.scalars().all()

        execution_records = []
        for instance in instances:
            execution_records.append(
                FlowExecutionRecord(
                    instanceId=str(instance.id),
                    sessionId=instance.session_id,
                    flowId=str(instance.flow_id),
                    flowName=flow_name,
                    currentStep=instance.current_step,
                    totalSteps=total_steps,
                    status=instance.status,
                    startedAt=instance.started_at.isoformat(),
                    updatedAt=instance.updated_at.isoformat(),
                    completedAt=instance.completed_at.isoformat() if instance.completed_at else None,
                )
            )

        logger.info(
            f"[AC-AISVC-104] Retrieved {len(execution_records)} execution records for flow={flow_id}, "
            f"tenant={tenant_id}, page={page}"
        )

        return execution_records, total_count

    async def _get_flow(self, flow_id: uuid.UUID) -> ScriptFlow | None:
        """Get flow by ID."""
        stmt = select(ScriptFlow).where(ScriptFlow.id == flow_id)
        result = await self._session.execute(stmt)
        return result.scalar_one_or_none()
