"""Repository protocols for transcript-related entities.

Contains Meeting, Segment, Summary, and Annotation repository protocols.
"""

from __future__ import annotations

from collections.abc import Sequence
from datetime import datetime
from typing import TYPE_CHECKING, Protocol

if TYPE_CHECKING:
    from uuid import UUID

    from noteflow.domain.entities import Annotation, Meeting, Segment, Summary
    from noteflow.domain.value_objects import AnnotationId, MeetingId, MeetingState


class MeetingRepository(Protocol):
    """Repository protocol for Meeting aggregate operations."""

    async def create(self, meeting: Meeting) -> Meeting:
        """Persist a new meeting.

        Args:
            meeting: Meeting to create.

        Returns:
            Created meeting with any generated fields populated.
        """
        ...

    async def get(self, meeting_id: MeetingId) -> Meeting | None:
        """Retrieve a meeting by ID.

        Args:
            meeting_id: Meeting identifier.

        Returns:
            Meeting if found, None otherwise.
        """
        ...

    async def update(self, meeting: Meeting) -> Meeting:
        """Update an existing meeting.

        Args:
            meeting: Meeting with updated fields.

        Returns:
            Updated meeting.

        Raises:
            ValueError: If meeting does not exist.
        """
        ...

    async def delete(self, meeting_id: MeetingId) -> bool:
        """Delete a meeting and all associated data.

        Args:
            meeting_id: Meeting identifier.

        Returns:
            True if deleted, False if not found.
        """
        ...

    async def list_all(
        self,
        states: list[MeetingState] | None = None,
        limit: int = 100,
        offset: int = 0,
        sort_desc: bool = True,
        project_id: UUID | None = None,
    ) -> tuple[Sequence[Meeting], int]:
        """List meetings with optional filtering.

        Args:
            states: Optional list of states to filter by.
            limit: Maximum number of meetings to return.
            offset: Number of meetings to skip.
            sort_desc: Sort by created_at descending if True.
            project_id: Optional project scope filter.

        Returns:
            Tuple of (meetings list, total count matching filter).
        """
        ...

    async def count_by_state(self, state: MeetingState) -> int:
        """Count meetings in a specific state.

        Args:
            state: Meeting state to count.

        Returns:
            Number of meetings in the specified state.
        """
        ...

    async def find_older_than(self, cutoff: datetime) -> Sequence[Meeting]:
        """Find completed meetings older than cutoff date.

        Args:
            cutoff: Cutoff datetime; meetings ended before this are returned.

        Returns:
            Sequence of meetings with ended_at before cutoff.
        """
        ...


class SegmentRepository(Protocol):
    """Repository protocol for Segment operations."""

    async def add(self, meeting_id: MeetingId, segment: Segment) -> Segment:
        """Add a segment to a meeting.

        Args:
            meeting_id: Meeting identifier.
            segment: Segment to add.

        Returns:
            Added segment with db_id populated.

        Raises:
            ValueError: If meeting does not exist.
        """
        ...

    async def add_batch(
        self,
        meeting_id: MeetingId,
        segments: Sequence[Segment],
    ) -> Sequence[Segment]:
        """Add multiple segments to a meeting in batch.

        Args:
            meeting_id: Meeting identifier.
            segments: Segments to add.

        Returns:
            Added segments with db_ids populated.

        Raises:
            ValueError: If meeting does not exist.
        """
        ...

    async def get_by_meeting(
        self,
        meeting_id: MeetingId,
        include_words: bool = True,
    ) -> Sequence[Segment]:
        """Get all segments for a meeting.

        Args:
            meeting_id: Meeting identifier.
            include_words: Include word-level timing.

        Returns:
            List of segments ordered by segment_id.
        """
        ...

    async def search_semantic(
        self,
        query_embedding: list[float],
        limit: int = 10,
        meeting_id: MeetingId | None = None,
    ) -> Sequence[tuple[Segment, float]]:
        """Search segments by semantic similarity.

        Args:
            query_embedding: Query embedding vector.
            limit: Maximum number of results.
            meeting_id: Optional meeting to restrict search to.

        Returns:
            List of (segment, similarity_score) tuples.
        """
        ...

    async def update_embedding(
        self,
        segment_db_id: int,
        embedding: list[float],
    ) -> None:
        """Update the embedding for a segment.

        Args:
            segment_db_id: Segment database primary key.
            embedding: New embedding vector.
        """
        ...

    async def compute_next_segment_id(self, meeting_id: MeetingId) -> int:
        """Compute the next segment_id for a meeting.

        Args:
            meeting_id: Meeting identifier.

        Returns:
            Next available segment_id (0 if meeting has no segments).
        """
        ...

    async def update_speaker(
        self,
        segment_db_id: int,
        speaker_id: str | None,
        speaker_confidence: float,
    ) -> None:
        """Update speaker diarization fields for a segment.

        Args:
            segment_db_id: Segment database primary key.
            speaker_id: Speaker identifier from diarization.
            speaker_confidence: Confidence of speaker assignment (0.0-1.0).
        """
        ...


class SummaryRepository(Protocol):
    """Repository protocol for Summary operations."""

    async def save(self, summary: Summary) -> Summary:
        """Save or update a meeting summary.

        Args:
            summary: Summary to save.

        Returns:
            Saved summary with db_id populated.
        """
        ...

    async def get_by_meeting(self, meeting_id: MeetingId) -> Summary | None:
        """Get summary for a meeting, or None if not found."""
        ...

    async def delete_by_meeting(self, meeting_id: MeetingId) -> bool:
        """Delete summary for a meeting. Returns True if deleted."""
        ...


class AnnotationRepository(Protocol):
    """Repository protocol for Annotation operations."""

    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.
        """
        ...

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

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

    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.
        """
        ...

    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.
        """
        ...

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

        Args:
            annotation_id: Annotation identifier.

        Returns:
            True if deleted, False if not found.
        """
        ...
