"""
Dashboard Service

Business logic for dashboard statistics and activity aggregation.
"""

import logging
from datetime import datetime, timezone
from typing import Any
from uuid import UUID

from sqlalchemy import func, exists, and_, or_
from sqlmodel import select

from backend.data.database import get_session_context
from backend.data.admin_models import (
    AdminDocument,
    AdminAnnotation,
    AnnotationHistory,
    TrainingTask,
    ModelVersion,
)

logger = logging.getLogger(__name__)

# Field class IDs for completeness calculation
# Identifiers: invoice_number (0) or ocr_number (3)
IDENTIFIER_CLASS_IDS = {0, 3}
# Payment accounts: bankgiro (4) or plusgiro (5)
PAYMENT_CLASS_IDS = {4, 5}


def is_annotation_complete(annotations: list[dict[str, Any]]) -> bool:
    """Check if a document's annotations are complete.

    A document is complete if it has:
    - At least one identifier field (invoice_number OR ocr_number)
    - At least one payment field (bankgiro OR plusgiro)

    Args:
        annotations: List of annotation dicts with class_id

    Returns:
        True if document has required fields
    """
    class_ids = {ann.get("class_id") for ann in annotations}

    has_identifier = bool(class_ids & IDENTIFIER_CLASS_IDS)
    has_payment = bool(class_ids & PAYMENT_CLASS_IDS)

    return has_identifier and has_payment


class DashboardStatsService:
    """Service for computing dashboard statistics."""

    def get_stats(self) -> dict[str, Any]:
        """Get dashboard statistics.

        Returns:
            Dict with total_documents, annotation_complete, annotation_incomplete,
            pending, and completeness_rate
        """
        with get_session_context() as session:
            # Total documents
            total = session.exec(
                select(func.count()).select_from(AdminDocument)
            ).one()

            # Pending documents (status in ['pending', 'auto_labeling'])
            pending = session.exec(
                select(func.count())
                .select_from(AdminDocument)
                .where(AdminDocument.status.in_(["pending", "auto_labeling"]))
            ).one()

            # Complete annotations: labeled + has identifier + has payment
            complete = self._count_complete(session)

            # Incomplete: labeled but not complete
            labeled_count = session.exec(
                select(func.count())
                .select_from(AdminDocument)
                .where(AdminDocument.status == "labeled")
            ).one()
            incomplete = labeled_count - complete

            # Calculate completeness rate
            total_assessed = complete + incomplete
            completeness_rate = (
                round(complete / total_assessed * 100, 2)
                if total_assessed > 0
                else 0.0
            )

            return {
                "total_documents": total,
                "annotation_complete": complete,
                "annotation_incomplete": incomplete,
                "pending": pending,
                "completeness_rate": completeness_rate,
            }

    def _count_complete(self, session) -> int:
        """Count documents with complete annotations.

        A document is complete if it:
        1. Has status = 'labeled'
        2. Has at least one identifier annotation (class_id 0 or 3)
        3. Has at least one payment annotation (class_id 4 or 5)
        """
        # Subquery for documents with identifier
        has_identifier = exists(
            select(1)
            .select_from(AdminAnnotation)
            .where(
                and_(
                    AdminAnnotation.document_id == AdminDocument.document_id,
                    AdminAnnotation.class_id.in_(IDENTIFIER_CLASS_IDS),
                )
            )
        )

        # Subquery for documents with payment
        has_payment = exists(
            select(1)
            .select_from(AdminAnnotation)
            .where(
                and_(
                    AdminAnnotation.document_id == AdminDocument.document_id,
                    AdminAnnotation.class_id.in_(PAYMENT_CLASS_IDS),
                )
            )
        )

        count = session.exec(
            select(func.count())
            .select_from(AdminDocument)
            .where(
                and_(
                    AdminDocument.status == "labeled",
                    has_identifier,
                    has_payment,
                )
            )
        ).one()

        return count


class DashboardActivityService:
    """Service for aggregating recent activities."""

    def get_recent_activities(self, limit: int = 10) -> list[dict[str, Any]]:
        """Get recent system activities.

        Aggregates from:
        - Document uploads
        - Annotation modifications
        - Training completions/failures
        - Model activations

        Args:
            limit: Maximum number of activities to return

        Returns:
            List of activity dicts sorted by timestamp DESC
        """
        activities = []

        with get_session_context() as session:
            # Document uploads (recent 10)
            uploads = session.exec(
                select(AdminDocument)
                .order_by(AdminDocument.created_at.desc())
                .limit(limit)
            ).all()

            for doc in uploads:
                activities.append({
                    "type": "document_uploaded",
                    "description": f"Uploaded {doc.filename}",
                    "timestamp": doc.created_at,
                    "metadata": {
                        "document_id": str(doc.document_id),
                        "filename": doc.filename,
                    },
                })

            # Annotation modifications (from history)
            modifications = session.exec(
                select(AnnotationHistory)
                .where(AnnotationHistory.action == "override")
                .order_by(AnnotationHistory.created_at.desc())
                .limit(limit)
            ).all()

            for mod in modifications:
                # Get document filename
                doc = session.get(AdminDocument, mod.document_id)
                filename = doc.filename if doc else "Unknown"
                field_name = ""
                if mod.new_value and isinstance(mod.new_value, dict):
                    field_name = mod.new_value.get("class_name", "")

                activities.append({
                    "type": "annotation_modified",
                    "description": f"Modified {filename} {field_name}".strip(),
                    "timestamp": mod.created_at,
                    "metadata": {
                        "annotation_id": str(mod.annotation_id),
                        "document_id": str(mod.document_id),
                        "field_name": field_name,
                    },
                })

            # Training completions and failures
            training_tasks = session.exec(
                select(TrainingTask)
                .where(TrainingTask.status.in_(["completed", "failed"]))
                .order_by(TrainingTask.updated_at.desc())
                .limit(limit)
            ).all()

            for task in training_tasks:
                if task.updated_at is None:
                    continue
                if task.status == "completed":
                    # Use metrics_mAP field directly
                    mAP = task.metrics_mAP or 0.0
                    activities.append({
                        "type": "training_completed",
                        "description": f"Training complete: {task.name}, mAP {mAP:.1%}",
                        "timestamp": task.updated_at,
                        "metadata": {
                            "task_id": str(task.task_id),
                            "task_name": task.name,
                            "mAP": mAP,
                        },
                    })
                else:
                    activities.append({
                        "type": "training_failed",
                        "description": f"Training failed: {task.name}",
                        "timestamp": task.updated_at,
                        "metadata": {
                            "task_id": str(task.task_id),
                            "task_name": task.name,
                            "error": task.error_message or "",
                        },
                    })

            # Model activations
            model_versions = session.exec(
                select(ModelVersion)
                .where(ModelVersion.activated_at.is_not(None))
                .order_by(ModelVersion.activated_at.desc())
                .limit(limit)
            ).all()

            for model in model_versions:
                if model.activated_at is None:
                    continue
                activities.append({
                    "type": "model_activated",
                    "description": f"Activated model {model.version}",
                    "timestamp": model.activated_at,
                    "metadata": {
                        "version_id": str(model.version_id),
                        "version": model.version,
                    },
                })

        # Sort all activities by timestamp DESC and return top N
        activities.sort(key=lambda x: x["timestamp"], reverse=True)
        return activities[:limit]
