"""Convert between ORM models and domain entities."""

from __future__ import annotations

from typing import TYPE_CHECKING

from noteflow.domain.entities import (
    ActionItem,
    Annotation,
    KeyPoint,
    Meeting,
    Segment,
    Summary,
)
from noteflow.domain.entities import (
    WordTiming as DomainWordTiming,
)
from noteflow.domain.value_objects import (
    AnnotationId,
    AnnotationType,
    MeetingId,
    MeetingState,
)

if TYPE_CHECKING:
    from noteflow.infrastructure.persistence.models import (
        ActionItemModel,
        AnnotationModel,
        KeyPointModel,
        MeetingModel,
        SegmentModel,
        SummaryModel,
        WordTimingModel,
    )


class OrmConverter:
    """Convert between ORM models and domain entities."""

    # --- WordTiming ---

    @staticmethod
    def word_timing_to_domain(model: WordTimingModel) -> DomainWordTiming:
        """Convert ORM WordTiming model to domain entity.

        Args:
            model: SQLAlchemy WordTimingModel instance.

        Returns:
            Domain WordTiming entity.

        Raises:
            ValueError: If timing validation fails during entity construction.
        """
        return DomainWordTiming(
            word=model.word,
            start_time=model.start_time,
            end_time=model.end_time,
            probability=model.probability,
        )

    @staticmethod
    def word_timing_to_orm_kwargs(
        word: DomainWordTiming, word_index: int
    ) -> dict[str, str | float | int]:
        """Convert domain WordTiming to ORM model kwargs.

        Return a dict of kwargs rather than instantiating WordTimingModel directly
        to avoid circular imports and allow the repository to handle ORM construction.

        Args:
            word: Domain WordTiming entity.
            word_index: Position of word in the segment.

        Returns:
            Dict with word, word_index, start_time, end_time, probability for ORM construction.
        """
        return {
            "word": word.word,
            "word_index": word_index,
            "start_time": word.start_time,
            "end_time": word.end_time,
            "probability": word.probability,
        }

    # --- Meeting ---

    @staticmethod
    def meeting_to_domain(model: MeetingModel) -> Meeting:
        """Convert ORM Meeting model to domain entity.

        Args:
            model: SQLAlchemy MeetingModel instance.

        Returns:
            Domain Meeting entity.
        """
        return Meeting(
            id=MeetingId(model.id),
            title=model.title,
            project_id=model.project_id,
            state=MeetingState(model.state),
            created_at=model.created_at,
            started_at=model.started_at,
            ended_at=model.ended_at,
            # Convert metadata values to strings for proto compatibility.
            # Proto map<string, string> requires string values; the ORM stores
            # JSONB which may contain non-string values from legacy data.
            metadata={k: str(v) for k, v in model.metadata_.items()},
            wrapped_dek=model.wrapped_dek,
            asset_path=model.asset_path,
            version=model.version,
        )

    # --- Segment ---

    @staticmethod
    def segment_to_domain(model: SegmentModel, include_words: bool = True) -> Segment:
        """Convert ORM Segment model to domain entity.

        Args:
            model: SQLAlchemy SegmentModel instance.
            include_words: Whether to include word-level timing.

        Returns:
            Domain Segment entity.
        """
        words: list[DomainWordTiming] = []
        if include_words:
            words = [OrmConverter.word_timing_to_domain(w) for w in model.words]

        embedding = list(model.embedding) if model.embedding is not None else None

        return Segment(
            segment_id=model.segment_id,
            text=model.text,
            start_time=model.start_time,
            end_time=model.end_time,
            meeting_id=MeetingId(model.meeting_id),
            words=words,
            language=model.language,
            language_confidence=model.language_confidence,
            avg_logprob=model.avg_logprob,
            no_speech_prob=model.no_speech_prob,
            embedding=embedding,
            speaker_id=model.speaker_id,
            speaker_confidence=model.speaker_confidence,
            db_id=model.id,
        )

    # --- Annotation ---

    @staticmethod
    def annotation_to_domain(model: AnnotationModel) -> Annotation:
        """Convert ORM Annotation model to domain entity.

        Args:
            model: SQLAlchemy AnnotationModel instance.

        Returns:
            Domain Annotation entity.
        """
        return Annotation(
            id=AnnotationId(model.annotation_id),
            meeting_id=MeetingId(model.meeting_id),
            annotation_type=AnnotationType(model.annotation_type),
            text=model.text,
            start_time=model.start_time,
            end_time=model.end_time,
            segment_ids=model.segment_ids,
            created_at=model.created_at,
            db_id=model.id,
        )

    # --- Summary ---

    @staticmethod
    def key_point_to_domain(model: KeyPointModel) -> KeyPoint:
        """Convert ORM KeyPoint model to domain entity.

        Args:
            model: SQLAlchemy KeyPointModel instance.

        Returns:
            Domain KeyPoint entity.
        """
        return KeyPoint(
            text=model.text,
            segment_ids=model.segment_ids,
            start_time=model.start_time,
            end_time=model.end_time,
            position=model.position,
            db_id=model.id,
        )

    @staticmethod
    def action_item_to_domain(model: ActionItemModel) -> ActionItem:
        """Convert ORM ActionItem model to domain entity.

        Args:
            model: SQLAlchemy ActionItemModel instance.

        Returns:
            Domain ActionItem entity.
        """
        return ActionItem(
            text=model.text,
            assignee=model.assignee,
            due_date=model.due_date,
            priority=model.priority,
            segment_ids=model.segment_ids,
            start_time=model.start_time,
            end_time=model.end_time,
            position=model.position,
            db_id=model.id,
        )

    @staticmethod
    def summary_to_domain(model: SummaryModel, meeting_id: MeetingId) -> Summary:
        """Convert ORM Summary model to domain entity.

        Args:
            model: SQLAlchemy SummaryModel instance.
            meeting_id: Meeting identifier (passed for type safety).

        Returns:
            Domain Summary entity.
        """
        return Summary(
            meeting_id=meeting_id,
            executive_summary=model.executive_summary or "",
            key_points=[OrmConverter.key_point_to_domain(kp) for kp in model.key_points],
            action_items=[OrmConverter.action_item_to_domain(ai) for ai in model.action_items],
            generated_at=model.generated_at,
            provider_name=model.provider_name,
            model_name=model.model_name,
            tokens_used=model.tokens_used,
            latency_ms=model.latency_ms,
            verification=dict(model.verification),
            db_id=model.id,
        )
