"""Async meetings handling for the Meemo Python client.

This module provides the AsyncMeetings class for accessing meeting data
from the Meemo External API using async/await, including meeting details,
transcripts, summaries, participants, and recordings.

Authors:
    GDP Labs

References:
    https://gdplabs.gitbook.io/meemo/resources/external-api-documentation
"""

import logging

from meemo._base import (
    _ERR_EXPECTED_DICT,
    _ERR_EXPECTED_LIST,
    _AsyncBaseService,
    _BaseMeetingsMixin,
)
from meemo.models import (
    CreateMeetingCalendarEvent,
    MeetingDetail,
    MeetingListResponse,
    ParticipantsResponse,
    RecordingResponse,
    SummaryResponse,
    Topic,
    TranscriptResponse,
)

logger = logging.getLogger(__name__)


class AsyncMeetings(_AsyncBaseService, _BaseMeetingsMixin):
    """Handles async Meeting API operations for the Meemo External API."""

    async def list_meetings(
        self,
        organization_id: int | None = None,
        title: str | None = None,
        participants: str | None = None,
        created_after: str | None = None,
        created_before: str | None = None,
        summary_complete: bool | None = None,
        from_calendar: bool | None = None,
        page: int | None = None,
        size: int | None = None,
        extra_headers: dict[str, str] | None = None,
    ) -> MeetingListResponse:
        """Asynchronously retrieve a paginated list of meetings.

        Fetches meetings sorted by start time (newest first) across all
        organizations the caller has access to, unless narrowed by filters.

        Args:
            organization_id: Restrict results to a single organization.
                Omit to include meetings from every accessible organization.
            title: Case-insensitive substring match on the meeting title.
            participants: Comma-separated user IDs to match against attendees.
            created_after: Lower bound for creation date (YYYY-MM-DD format).
            created_before: Upper bound for creation date (YYYY-MM-DD format).
            summary_complete: When set, only return meetings whose summary
                generation has (or has not) finished.
            from_calendar: ``True`` for calendar-synced meetings,
                ``False`` for manually created ones.
            page: 1-based page index for pagination.
            size: Maximum number of items per page (defaults to 10).
            extra_headers: Additional HTTP headers to send with the request.

        Returns:
            MeetingListResponse: Paginated meeting results.

        Raises:
            httpx.HTTPStatusError: On a non-success HTTP status.
        """
        logger.debug("Listing meetings")

        url = self._meeting_url()
        headers = await self._prepare_headers(extra_headers)
        params = self._build_list_params(
            organization_id=organization_id,
            title=title,
            participants=participants,
            created_after=created_after,
            created_before=created_before,
            summary_complete=summary_complete,
            from_calendar=from_calendar,
            page=page,
            size=size,
        )

        response_data = await self._make_request("GET", url, headers, params=params)
        if not isinstance(response_data, dict):
            raise TypeError(_ERR_EXPECTED_DICT)
        return MeetingListResponse(**response_data)

    async def get_meeting(
        self,
        meeting_id: int,
        extra_headers: dict[str, str] | None = None,
    ) -> MeetingDetail:
        """Asynchronously fetch details for a single meeting.

        The caller must have access to the organization that owns
        the requested meeting.

        Args:
            meeting_id: Numeric identifier of the meeting to retrieve.
            extra_headers: Additional HTTP headers to send with the request.

        Returns:
            MeetingDetail: Full meeting metadata.

        Raises:
            ValueError: When ``meeting_id`` is zero or negative.
            httpx.HTTPStatusError: On a non-success HTTP status.
        """
        self._validate_meeting_id(meeting_id)
        logger.debug("Retrieving meeting: %d", meeting_id)

        url = self._meeting_url(f"{meeting_id}/")
        headers = await self._prepare_headers(extra_headers)

        response_data = await self._make_request("GET", url, headers)
        if not isinstance(response_data, dict):
            raise TypeError(_ERR_EXPECTED_DICT)
        return MeetingDetail(**response_data)

    async def get_transcript(
        self,
        meeting_id: int,
        extra_headers: dict[str, str] | None = None,
    ) -> TranscriptResponse:
        """Asynchronously fetch the transcript for a meeting.

        Retrieves time-ordered transcript segments as a JSON array.
        Segments marked as confidential are automatically excluded.

        Args:
            meeting_id: Numeric identifier of the meeting.
            extra_headers: Additional HTTP headers to send with the request.

        Returns:
            TranscriptResponse: Ordered transcript segments.

        Raises:
            ValueError: When ``meeting_id`` is zero or negative.
            httpx.HTTPStatusError: On a non-success HTTP status.
        """
        self._validate_meeting_id(meeting_id)
        logger.debug("Retrieving transcript for meeting: %d", meeting_id)

        url = self._meeting_url(f"{meeting_id}/transcript/")
        headers = await self._prepare_headers(extra_headers)

        response_data = await self._make_request("GET", url, headers)
        if not isinstance(response_data, dict):
            raise TypeError(_ERR_EXPECTED_DICT)
        return TranscriptResponse(**response_data)

    async def get_summary(
        self,
        meeting_id: int,
        extra_headers: dict[str, str] | None = None,
    ) -> SummaryResponse:
        """Asynchronously fetch the summary for a meeting.

        Returns notes, keywords, and a summary that may be either a
        Markdown string (current format) or a structured JSON object
        (legacy format).

        Args:
            meeting_id: Numeric identifier of the meeting.
            extra_headers: Additional HTTP headers to send with the request.

        Returns:
            SummaryResponse: Summary, notes, and keywords.

        Raises:
            ValueError: When ``meeting_id`` is zero or negative.
            httpx.HTTPStatusError: On a non-success HTTP status.
        """
        self._validate_meeting_id(meeting_id)
        logger.debug("Retrieving summary for meeting: %d", meeting_id)

        url = self._meeting_url(f"{meeting_id}/summary/")
        headers = await self._prepare_headers(extra_headers)

        response_data = await self._make_request("GET", url, headers)
        if not isinstance(response_data, dict):
            raise TypeError(_ERR_EXPECTED_DICT)
        return SummaryResponse(**response_data)

    async def get_participants(
        self,
        meeting_id: int,
        extra_headers: dict[str, str] | None = None,
    ) -> ParticipantsResponse:
        """Asynchronously fetch the participant list for a meeting.

        Args:
            meeting_id: Numeric identifier of the meeting.
            extra_headers: Additional HTTP headers to send with the request.

        Returns:
            ParticipantsResponse: Attendee information for the meeting.

        Raises:
            ValueError: When ``meeting_id`` is zero or negative.
            httpx.HTTPStatusError: On a non-success HTTP status.
        """
        self._validate_meeting_id(meeting_id)
        logger.debug("Retrieving participants for meeting: %d", meeting_id)

        url = self._meeting_url(f"{meeting_id}/participants/")
        headers = await self._prepare_headers(extra_headers)

        response_data = await self._make_request("GET", url, headers)
        if not isinstance(response_data, dict):
            raise TypeError(_ERR_EXPECTED_DICT)
        return ParticipantsResponse(**response_data)

    async def get_recording(
        self,
        meeting_id: int,
        extra_headers: dict[str, str] | None = None,
    ) -> RecordingResponse:
        """Asynchronously fetch the recording URL for a meeting.

        Args:
            meeting_id: Numeric identifier of the meeting.
            extra_headers: Additional HTTP headers to send with the request.

        Returns:
            RecordingResponse: Pre-signed URL and recording metadata.

        Raises:
            ValueError: When ``meeting_id`` is zero or negative.
            httpx.HTTPStatusError: On a non-success HTTP status.
        """
        self._validate_meeting_id(meeting_id)
        logger.debug("Retrieving recording for meeting: %d", meeting_id)

        url = self._meeting_url(f"{meeting_id}/recording/")
        headers = await self._prepare_headers(extra_headers)

        response_data = await self._make_request("GET", url, headers)
        if not isinstance(response_data, dict):
            raise TypeError(_ERR_EXPECTED_DICT)
        return RecordingResponse(**response_data)

    async def create_meeting(
        self,
        organization_id: int,
        title: str | None = None,
        start_time: str | None = None,
        user_token: str | None = None,
        topic_id: int | None = None,
        participants: str | None = None,
        bot_meeting_url: str | None = None,
        bot_join_at: str | None = None,
        file_url: str | None = None,
        speaker_count: int | None = None,
        calendar_event: CreateMeetingCalendarEvent | None = None,
        extra_headers: dict[str, str] | None = None,
    ) -> MeetingDetail:
        """Asynchronously create a new meeting record.

        Args:
            organization_id: ID of the organization that will own the meeting.
            title: Optional display name for the meeting.
            start_time: ISO 8601 timestamp for when the meeting begins.
            user_token: Token identifying the user who creates the meeting.
            topic_id: Link the meeting to an existing topic.
            participants: Comma-separated user IDs of attendees.
            bot_meeting_url: Live-meeting URL for the bot to join.
                Cannot be combined with ``file_url``.
            bot_join_at: Schedule the bot to join at a future time (ISO 8601).
                If omitted, the bot joins immediately.
            file_url: URL of a pre-recorded audio/video file to process.
                Cannot be combined with ``bot_meeting_url``.
            speaker_count: Expected number of distinct speakers (minimum 1).
            calendar_event: Calendar event context to attach.
            extra_headers: Additional HTTP headers to send with the request.

        Returns:
            MeetingDetail: The newly created meeting record.

        Raises:
            ValueError: On invalid parameters (e.g. non-positive IDs).
            httpx.HTTPStatusError: On a non-success HTTP status.
        """
        self._validate_create_meeting_params(
            organization_id=organization_id,
            speaker_count=speaker_count,
            bot_meeting_url=bot_meeting_url,
            bot_join_at=bot_join_at,
            file_url=file_url,
        )
        logger.debug("Creating meeting for organization: %d", organization_id)

        url = self._meeting_url()
        headers = await self._prepare_headers(extra_headers)
        headers["Content-Type"] = "application/json"

        json_data = self._build_create_body(
            organization_id=organization_id,
            title=title,
            start_time=start_time,
            user_token=user_token,
            topic_id=topic_id,
            participants=participants,
            bot_meeting_url=bot_meeting_url,
            bot_join_at=bot_join_at,
            file_url=file_url,
            speaker_count=speaker_count,
            calendar_event=calendar_event,
        )

        response_data = await self._make_request("POST", url, headers, json_data=json_data)
        if not isinstance(response_data, dict):
            raise TypeError(_ERR_EXPECTED_DICT)
        return MeetingDetail(**response_data)

    async def list_topics(
        self,
        organization_id: int | None = None,
        extra_headers: dict[str, str] | None = None,
    ) -> list[Topic]:
        """Asynchronously retrieve the list of available topics.

        Args:
            organization_id: Restrict results to a single organization.
            extra_headers: Additional HTTP headers to send with the request.

        Returns:
            list[Topic]: Available topic objects.

        Raises:
            httpx.HTTPStatusError: On a non-success HTTP status.
        """
        logger.debug("Listing topics")

        url = self._topics_url()
        headers = await self._prepare_headers(extra_headers)
        params = {"organization_id": organization_id} if organization_id is not None else None

        response_data = await self._make_request("GET", url, headers, params=params)
        if not isinstance(response_data, list):
            raise TypeError(_ERR_EXPECTED_LIST)
        return [Topic(**item) for item in response_data]
