"""
Model Version Repository

Handles model version operations following Single Responsibility Principle.
"""

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

from sqlalchemy import func
from sqlmodel import select

from backend.data.database import get_session_context
from backend.data.admin_models import ModelVersion
from backend.data.repositories.base import BaseRepository

logger = logging.getLogger(__name__)


class ModelVersionRepository(BaseRepository[ModelVersion]):
    """Repository for model version management.

    Handles:
    - Model version CRUD operations
    - Model activation/deactivation
    - Active model resolution
    """

    def create(
        self,
        version: str,
        name: str,
        model_path: str,
        description: str | None = None,
        task_id: str | UUID | None = None,
        dataset_id: str | UUID | None = None,
        metrics_mAP: float | None = None,
        metrics_precision: float | None = None,
        metrics_recall: float | None = None,
        document_count: int = 0,
        training_config: dict[str, Any] | None = None,
        file_size: int | None = None,
        trained_at: datetime | None = None,
        model_type: str = "base",
        base_model_version_id: str | UUID | None = None,
        base_training_dataset_id: str | UUID | None = None,
        gating_status: str = "pending",
    ) -> ModelVersion:
        """Create a new model version."""
        with get_session_context() as session:
            model = ModelVersion(
                version=version,
                name=name,
                model_path=model_path,
                description=description,
                task_id=UUID(str(task_id)) if task_id else None,
                dataset_id=UUID(str(dataset_id)) if dataset_id else None,
                metrics_mAP=metrics_mAP,
                metrics_precision=metrics_precision,
                metrics_recall=metrics_recall,
                document_count=document_count,
                training_config=training_config,
                file_size=file_size,
                trained_at=trained_at,
                model_type=model_type,
                base_model_version_id=UUID(str(base_model_version_id)) if base_model_version_id else None,
                base_training_dataset_id=UUID(str(base_training_dataset_id)) if base_training_dataset_id else None,
                gating_status=gating_status,
            )
            session.add(model)
            session.commit()
            session.refresh(model)
            session.expunge(model)
            return model

    def get(self, version_id: str | UUID) -> ModelVersion | None:
        """Get a model version by ID."""
        with get_session_context() as session:
            model = session.get(ModelVersion, UUID(str(version_id)))
            if model:
                session.expunge(model)
            return model

    def get_paginated(
        self,
        status: str | None = None,
        limit: int = 20,
        offset: int = 0,
    ) -> tuple[list[ModelVersion], int]:
        """List model versions with optional status filter."""
        with get_session_context() as session:
            query = select(ModelVersion)
            count_query = select(func.count()).select_from(ModelVersion)
            if status:
                query = query.where(ModelVersion.status == status)
                count_query = count_query.where(ModelVersion.status == status)
            total = session.exec(count_query).one()
            models = session.exec(
                query.order_by(ModelVersion.created_at.desc()).offset(offset).limit(limit)
            ).all()
            for m in models:
                session.expunge(m)
            return list(models), total

    def get_active(self) -> ModelVersion | None:
        """Get the currently active model version for inference."""
        with get_session_context() as session:
            result = session.exec(
                select(ModelVersion).where(ModelVersion.is_active == True)
            ).first()
            if result:
                session.expunge(result)
            return result

    def activate(self, version_id: str | UUID) -> ModelVersion | None:
        """Activate a model version for inference (deactivates all others)."""
        with get_session_context() as session:
            all_versions = session.exec(
                select(ModelVersion).where(ModelVersion.is_active == True)
            ).all()
            for v in all_versions:
                v.is_active = False
                v.status = "inactive"
                v.updated_at = datetime.utcnow()
                session.add(v)

            model = session.get(ModelVersion, UUID(str(version_id)))
            if not model:
                return None
            model.is_active = True
            model.status = "active"
            model.activated_at = datetime.utcnow()
            model.updated_at = datetime.utcnow()
            session.add(model)
            session.commit()
            session.refresh(model)
            session.expunge(model)
            return model

    def deactivate(self, version_id: str | UUID) -> ModelVersion | None:
        """Deactivate a model version."""
        with get_session_context() as session:
            model = session.get(ModelVersion, UUID(str(version_id)))
            if not model:
                return None
            model.is_active = False
            model.status = "inactive"
            model.updated_at = datetime.utcnow()
            session.add(model)
            session.commit()
            session.refresh(model)
            session.expunge(model)
            return model

    def update(
        self,
        version_id: str | UUID,
        name: str | None = None,
        description: str | None = None,
        status: str | None = None,
    ) -> ModelVersion | None:
        """Update model version metadata."""
        with get_session_context() as session:
            model = session.get(ModelVersion, UUID(str(version_id)))
            if not model:
                return None
            if name is not None:
                model.name = name
            if description is not None:
                model.description = description
            if status is not None:
                model.status = status
            model.updated_at = datetime.utcnow()
            session.add(model)
            session.commit()
            session.refresh(model)
            session.expunge(model)
            return model

    def archive(self, version_id: str | UUID) -> ModelVersion | None:
        """Archive a model version."""
        with get_session_context() as session:
            model = session.get(ModelVersion, UUID(str(version_id)))
            if not model:
                return None
            if model.is_active:
                return None
            model.status = "archived"
            model.updated_at = datetime.utcnow()
            session.add(model)
            session.commit()
            session.refresh(model)
            session.expunge(model)
            return model

    def delete(self, version_id: str | UUID) -> bool:
        """Delete a model version."""
        with get_session_context() as session:
            model = session.get(ModelVersion, UUID(str(version_id)))
            if not model:
                return False
            if model.is_active:
                return False
            session.delete(model)
            session.commit()
            return True
