"""
SSE (Server-Sent Events) stream parser for GetBiji API.

Handles parsing of streaming responses for link notes and audio notes.
"""

import json
import re
from typing import Any, Dict, Generator, List, Optional, Tuple

import requests

from .exceptions import GetBijiError
from .models import AudioSectionInfo, Note, ProgressItem


def parse_sse_response(
    response: requests.Response,
) -> Generator[Tuple[int, Dict[str, Any]], None, None]:
    """
    Parse SSE (Server-Sent Events) format streaming response.

    Args:
        response: The requests.Response object with streaming enabled

    Yields:
        Tuples of (msg_type, data) where:
        - msg_type: Integer message type
            - -1: Initialization (returns note_id)
            - -2: Final result (complete note object)
            - 6: Progress update for link notes
            - -6: Chapter progress for audio notes
        - data: Dictionary containing the message data

    Example:
        >>> for msg_type, data in parse_sse_response(response):
        ...     if msg_type == -1:
        ...         note_id = data.get('note_id')
        ...     elif msg_type == 6:
        ...         progress = parse_link_note_progress(data)
        ...     elif msg_type == -2:
        ...         note = Note.from_dict(data)
    """
    if not getattr(response, "stream", False):
        raise GetBijiError("Response does not have streaming enabled")

    buffer = ""
    for chunk in response.iter_content(chunk_size=1024, decode_unicode=True):
        if chunk:
            buffer += chunk
            # Process complete events
            while True:
                # Find event boundary (double newline)
                event_end = buffer.find("\n\n")
                if event_end == -1:
                    break

                event_text = buffer[:event_end]
                buffer = buffer[event_end + 2 :]

                # Parse the event
                lines = event_text.split("\n")
                data_line = None
                for line in lines:
                    if line.startswith("data:"):
                        data_line = line[5:].strip()
                        break

                if data_line:
                    try:
                        data = json.loads(data_line)
                        msg_type = data.get("msg_type")
                        if msg_type is not None:
                            yield msg_type, data.get("data", {})
                    except json.JSONDecodeError:
                        # Skip invalid JSON
                        continue


def parse_link_note_progress(data: Any) -> List[ProgressItem]:
    """
    Parse link note processing progress from streaming data.

    Args:
        data: Data from msg_type=6 message, can be a list or string

    Returns:
        List of ProgressItem objects

    Example:
        [
            ProgressItem(msg="正在浏览链接", process_name="received_req", process_status="end"),
            ProgressItem(msg="链接分析中", process_name="analysis_web_page", process_status="end"),
        ]
    """
    progress_items: List[ProgressItem] = []

    # Handle both list format and string format
    if isinstance(data, list):
        for item in data:
            if isinstance(item, dict):
                progress_items.append(
                    ProgressItem(
                        msg=item.get("msg", ""),
                        process_name=item.get("processName", ""),
                        process_status=item.get("processStatus", ""),
                    )
                )
    elif isinstance(data, str):
        # String format might contain multiple progress updates
        # Try to extract JSON objects from the string
        pass  # String format handling if needed

    return progress_items


def parse_audio_note_sections(content: str) -> List[AudioSectionInfo]:
    """
    Parse chapter/section information from audio note content.

    The content contains markdown-formatted chapters like:
    [00:05:23](https://getnotes.seek:323000) **Chapter Title**

    Args:
        content: The full note content text

    Returns:
        List of AudioSectionInfo objects with timestamp and title
    """
    sections: List[AudioSectionInfo] = []

    # Pattern to match chapter entries
    # [HH:MM:SS](https://getnotes.seek:milliseconds) **Title**
    pattern = r"\[(\d{2}:\d{2}:\d{2})\]\(https://getnotes\.seek:(\d+)\)\s+\*\*(.+?)\*\*"

    for match in re.finditer(pattern, content):
        timestamp = match.group(1)
        milliseconds = int(match.group(2))
        title = match.group(3)

        # Try to get description from following text
        # (simplified - in practice would need more complex parsing)

        sections.append(
            AudioSectionInfo(
                title=title,
                description="",
                timestamp=timestamp,
                milliseconds=milliseconds,
            )
        )

    return sections


def format_timestamp(milliseconds: int) -> str:
    """
    Convert milliseconds to HH:MM:SS format.

    Args:
        milliseconds: Time in milliseconds

    Returns:
        Formatted timestamp string
    """
    seconds = milliseconds // 1000
    hours = seconds // 3600
    minutes = (seconds % 3600) // 60
    secs = seconds % 60

    return f"{hours:02d}:{minutes:02d}:{secs:02d}"


def parse_timestamp(timestamp_str: str) -> int:
    """
    Convert HH:MM:SS format to milliseconds.

    Args:
        timestamp_str: Timestamp in HH:MM:SS format

    Returns:
        Time in milliseconds
    """
    parts = timestamp_str.split(":")
    if len(parts) == 3:
        hours, minutes, seconds = map(int, parts)
        return (hours * 3600 + minutes * 60 + seconds) * 1000
    return 0


class StreamCallback:
    """
    Base class for streaming callbacks.

    Subclass this and override the methods to handle streaming events.
    """

    def on_init(self, note_id: str):
        """Called when streaming is initialized with the note_id."""
        pass

    def on_progress(self, progress: List[ProgressItem]):
        """Called when progress updates are available."""
        pass

    def on_chapter(self, chapter: AudioSectionInfo):
        """Called when a new chapter is detected (for audio notes)."""
        pass

    def on_complete(self, note: Note):
        """Called when the note is complete."""
        pass

    def on_error(self, error: str):
        """Called when an error occurs during streaming."""
        pass


def handle_stream_response(
    response: requests.Response,
    callback: Optional[StreamCallback] = None,
    note_id: Optional[str] = None,
) -> Note:
    """
    Handle a streaming response from note creation.

    This is a convenience function that processes the SSE stream
    and calls the appropriate callback methods.

    Args:
        response: The streaming response
        callback: Optional StreamCallback instance
        note_id: Optional note_id to use

    Returns:
        The completed Note object

    Raises:
        GetBijiError: If streaming fails or returns no note
    """
    final_note = None

    for msg_type, data in parse_sse_response(response):
        if msg_type == -1:
            # Initialization
            if callback:
                note_id = data.get("note_id")
                if note_id:
                    callback.on_init(note_id)
        elif msg_type == 6:
            # Link note progress
            if callback:
                progress = parse_link_note_progress(data)
                callback.on_progress(progress)
        elif msg_type == -6:
            # Audio note chapter progress (simplified handling)
            if callback and data:
                # Extract chapter info if available
                pass
        elif msg_type == -2:
            # Final result
            final_note = Note.from_dict(data)
            if callback:
                callback.on_complete(final_note)
            break

    if final_note is None:
        raise GetBijiError("Streaming completed without receiving a note")

    return final_note
