"""SQLAlchemy implementation of AnnotationRepository."""

from __future__ import annotations

from collections.abc import Sequence
from typing import TYPE_CHECKING
from uuid import UUID

from sqlalchemy import and_, delete, or_, select

from noteflow.domain.entities import Annotation
from noteflow.domain.value_objects import AnnotationId
from noteflow.infrastructure.converters import OrmConverter
from noteflow.infrastructure.persistence.models import AnnotationModel
from noteflow.infrastructure.persistence.repositories._base import BaseRepository

if TYPE_CHECKING:
    from noteflow.domain.value_objects import MeetingId


class SqlAlchemyAnnotationRepository(BaseRepository):
    """SQLAlchemy implementation of AnnotationRepository."""

    async def add(self, annotation: Annotation) -> Annotation:
        """Add an annotation to a meeting.

        Args:
            annotation: Annotation to add.

        Returns:
            Added annotation with db_id populated.

        Raises:
            ValueError: If meeting does not exist.
        """
        model = AnnotationModel()
        model.annotation_id = UUID(str(annotation.id))
        model.meeting_id = UUID(str(annotation.meeting_id))
        model.annotation_type = annotation.annotation_type.value
        model.text = annotation.text
        model.start_time = annotation.start_time
        model.end_time = annotation.end_time
        model.segment_ids = annotation.segment_ids
        model.created_at = annotation.created_at
        self._session.add(model)
        await self._session.flush()
        annotation.db_id = model.id
        return annotation

    async def get(self, annotation_id: AnnotationId) -> Annotation | None:
        """Retrieve an annotation by ID.

        Args:
            annotation_id: Annotation identifier.

        Returns:
            Annotation if found, None otherwise.
        """
        stmt = select(AnnotationModel).where(
            AnnotationModel.annotation_id == UUID(str(annotation_id))
        )
        model = await self._execute_scalar(stmt)

        return None if model is None else OrmConverter.annotation_to_domain(model)

    async def get_by_meeting(
        self,
        meeting_id: MeetingId,
    ) -> Sequence[Annotation]:
        """Get all annotations for a meeting.

        Args:
            meeting_id: Meeting identifier.

        Returns:
            List of annotations ordered by start_time.
        """
        stmt = (
            select(AnnotationModel)
            .where(AnnotationModel.meeting_id == UUID(str(meeting_id)))
            .order_by(AnnotationModel.start_time)
        )
        models = await self._execute_scalars(stmt)

        return [OrmConverter.annotation_to_domain(model) for model in models]

    async def get_by_time_range(
        self,
        meeting_id: MeetingId,
        start_time: float,
        end_time: float,
    ) -> Sequence[Annotation]:
        """Get annotations within a time range.

        Args:
            meeting_id: Meeting identifier.
            start_time: Start of time range in seconds.
            end_time: End of time range in seconds.

        Returns:
            List of annotations overlapping the time range.
        """
        # Find annotations that overlap with the given time range
        stmt = (
            select(AnnotationModel)
            .where(
                and_(
                    AnnotationModel.meeting_id == UUID(str(meeting_id)),
                    or_(
                        # Annotation starts within range
                        and_(
                            AnnotationModel.start_time >= start_time,
                            AnnotationModel.start_time <= end_time,
                        ),
                        # Annotation ends within range
                        and_(
                            AnnotationModel.end_time >= start_time,
                            AnnotationModel.end_time <= end_time,
                        ),
                        # Annotation spans entire range
                        and_(
                            AnnotationModel.start_time <= start_time,
                            AnnotationModel.end_time >= end_time,
                        ),
                    ),
                )
            )
            .order_by(AnnotationModel.start_time)
        )
        models = await self._execute_scalars(stmt)

        return [OrmConverter.annotation_to_domain(model) for model in models]

    async def update(self, annotation: Annotation) -> Annotation:
        """Update an existing annotation.

        Args:
            annotation: Annotation with updated fields.

        Returns:
            Updated annotation.

        Raises:
            ValueError: If annotation does not exist.
        """
        stmt = select(AnnotationModel).where(
            AnnotationModel.annotation_id == UUID(str(annotation.id))
        )
        model = await self._execute_scalar(stmt)

        if model is None:
            raise ValueError(f"Annotation {annotation.id} not found")

        model.annotation_type = annotation.annotation_type.value
        model.text = annotation.text
        model.start_time = annotation.start_time
        model.end_time = annotation.end_time
        model.segment_ids = annotation.segment_ids

        await self._session.flush()
        return annotation

    async def delete(self, annotation_id: AnnotationId) -> bool:
        """Delete an annotation.

        Args:
            annotation_id: Annotation identifier.

        Returns:
            True if deleted, False if not found.
        """
        stmt = select(AnnotationModel).where(
            AnnotationModel.annotation_id == UUID(str(annotation_id))
        )
        model = await self._execute_scalar(stmt)

        if model is None:
            return False

        await self._session.execute(delete(AnnotationModel).where(AnnotationModel.id == model.id))
        await self._session.flush()
        return True
