"""Export service for Trax platform.

This module provides export functionality for transcripts in various formats:
JSON, TXT, SRT, and Markdown with proper error handling and file management.
"""

import json
import logging
from datetime import datetime, timezone
from enum import Enum
from pathlib import Path
from typing import Any, Dict, List, Optional, Protocol, runtime_checkable

from ..base.services import BaseService
from ..config import config

logger = logging.getLogger(__name__)


class ExportFormat(Enum):
    """Supported export formats."""
    
    JSON = "json"
    TXT = "txt"
    SRT = "srt"
    MARKDOWN = "md"


class ExportError(Exception):
    """Base exception for export errors."""
    pass


@runtime_checkable
class MediaServiceProtocol(Protocol):
    """Protocol for media service dependency."""
    
    async def get_by_id(self, media_id: str) -> Optional[Dict[str, Any]]:
        """Get media file by ID."""
        ...


class ExportService(BaseService):
    """Service for exporting transcripts in various formats."""

    def __init__(self, export_dir: Optional[Path] = None):
        """Initialize ExportService.
        
        Args:
            export_dir: Default export directory. If None, uses 'exports' in current directory.
        """
        super().__init__(name="ExportService")
        self.export_dir = export_dir or Path("exports")
        self.export_dir.mkdir(exist_ok=True)

    async def _initialize_impl(self) -> None:
        """Initialize the export service."""
        # Export service doesn't need special initialization
        pass

    async def export_transcript(
        self,
        transcript: Dict[str, Any],
        format: ExportFormat,
        output_path: Optional[Path] = None
    ) -> Path:
        """Export transcript to specified format.
        
        Args:
            transcript: Transcript data dictionary
            format: Export format (JSON, TXT, SRT, MARKDOWN)
            output_path: Output file path. If None, generates default path.
            
        Returns:
            Path to exported file
            
        Raises:
            ExportError: If export fails
        """
        try:
            if output_path is None:
                output_path = await self._generate_output_path(transcript, format)
            
            # Ensure output directory exists
            output_path.parent.mkdir(parents=True, exist_ok=True)
            
            if format == ExportFormat.JSON:
                await self._export_json(transcript, output_path)
            elif format == ExportFormat.TXT:
                await self._export_txt(transcript, output_path)
            elif format == ExportFormat.SRT:
                await self._export_srt(transcript, output_path)
            elif format == ExportFormat.MARKDOWN:
                await self._export_markdown(transcript, output_path)
            else:
                raise ExportError(f"Unsupported export format: {format}")
            
            logger.info(f"Exported transcript to {output_path}")
            return output_path
            
        except Exception as e:
            logger.error(f"Export error: {str(e)}")
            raise ExportError(f"Export error: {str(e)}") from e

    async def batch_export(
        self,
        transcripts: List[Dict[str, Any]],
        format: ExportFormat,
        output_dir: Optional[Path] = None
    ) -> List[Optional[Path]]:
        """Export multiple transcripts in batch.
        
        Args:
            transcripts: List of transcript data dictionaries
            format: Export format for all transcripts
            output_dir: Output directory. If None, uses default export directory.
            
        Returns:
            List of exported file paths. None for failed exports.
        """
        if output_dir is None:
            output_dir = self.export_dir
        
        output_dir.mkdir(parents=True, exist_ok=True)
        results = []
        
        for i, transcript in enumerate(transcripts):
            try:
                # Validate transcript before processing
                if not self._validate_transcript(transcript):
                    logger.error(f"Invalid transcript structure for transcript {i}")
                    results.append(None)
                    continue
                
                # Generate filename based on transcript ID or index
                transcript_id = transcript.get("id", f"transcript_{i}")
                filename = f"{transcript_id}.{format.value}"
                output_path = output_dir / filename
                
                result_path = await self.export_transcript(
                    transcript=transcript,
                    format=format,
                    output_path=output_path
                )
                results.append(result_path)
                
            except Exception as e:
                logger.error(f"Batch export failed for transcript {i}: {str(e)}")
                results.append(None)
        
        return results

    async def _generate_output_path(
        self,
        transcript: Dict[str, Any],
        format: ExportFormat
    ) -> Path:
        """Generate default output path based on transcript metadata.
        
        Args:
            transcript: Transcript data
            format: Export format
            
        Returns:
            Generated output path
        """
        # Try to get filename from media file
        media_file_id = transcript.get("media_file_id")
        if media_file_id:
            media_file = await self._get_media_file(media_file_id)
            if media_file and media_file.get("local_path"):
                filename = Path(media_file["local_path"]).stem
            else:
                filename = transcript.get("id", "transcript")
        else:
            filename = transcript.get("id", "transcript")
        
        return self.export_dir / f"{filename}.{format.value}"

    async def _get_media_file(self, media_id: str) -> Optional[Dict[str, Any]]:
        """Get media file information.
        
        Args:
            media_id: Media file ID
            
        Returns:
            Media file data or None if not found
        """
        # This would typically use a media service dependency
        # For now, return None to use fallback naming
        return None

    async def _export_json(self, transcript: Dict[str, Any], output_path: Path) -> None:
        """Export transcript as JSON with full data.
        
        Args:
            transcript: Transcript data
            output_path: Output file path
        """
        with open(output_path, "w", encoding="utf-8") as f:
            json.dump(transcript, f, indent=2, ensure_ascii=False)

    async def _export_txt(self, transcript: Dict[str, Any], output_path: Path) -> None:
        """Export transcript as plain text.
        
        Args:
            transcript: Transcript data
            output_path: Output file path
        """
        text_content = transcript.get("content", {}).get("text", "")
        with open(output_path, "w", encoding="utf-8") as f:
            f.write(text_content)

    def _validate_transcript(self, transcript: Dict[str, Any]) -> bool:
        """Validate transcript data structure.
        
        Args:
            transcript: Transcript data to validate
            
        Returns:
            True if valid, False otherwise
        """
        # Check for required fields
        if not isinstance(transcript, dict):
            return False
        
        # Must have either content.text or segments
        has_content = (
            isinstance(transcript.get("content"), dict) and 
            transcript["content"].get("text")
        )
        has_segments = isinstance(transcript.get("segments"), list)
        
        return has_content or has_segments

    async def _export_srt(self, transcript: Dict[str, Any], output_path: Path) -> None:
        """Export transcript as SRT format with timestamps.
        
        Args:
            transcript: Transcript data
            output_path: Output file path
        """
        srt_content = convert_to_srt(transcript)
        with open(output_path, "w", encoding="utf-8") as f:
            f.write(srt_content)

    async def _export_markdown(self, transcript: Dict[str, Any], output_path: Path) -> None:
        """Export transcript as Markdown with formatting.
        
        Args:
            transcript: Transcript data
            output_path: Output file path
        """
        md_content = convert_to_markdown(transcript)
        with open(output_path, "w", encoding="utf-8") as f:
            f.write(md_content)


def format_timestamp(seconds: float) -> str:
    """Format seconds as SRT timestamp (HH:MM:SS,mmm).
    
    Args:
        seconds: Time in seconds
        
    Returns:
        Formatted timestamp string
    """
    hours = int(seconds / 3600)
    minutes = int((seconds % 3600) / 60)
    secs = seconds % 60
    # Use round() to handle floating point precision issues
    milliseconds = round((secs - int(secs)) * 1000)
    
    return f"{hours:02d}:{minutes:02d}:{int(secs):02d},{milliseconds:03d}"


def format_duration(seconds: float) -> str:
    """Format seconds as readable duration (HH:MM:SS).
    
    Args:
        seconds: Time in seconds
        
    Returns:
        Formatted duration string
    """
    hours = int(seconds / 3600)
    minutes = int((seconds % 3600) / 60)
    secs = int(seconds % 60)
    
    if hours > 0:
        return f"{hours:02d}:{minutes:02d}:{secs:02d}"
    else:
        return f"{minutes:02d}:{secs:02d}"


def convert_to_srt(transcript: Dict[str, Any]) -> str:
    """Convert transcript to SRT format.
    
    Args:
        transcript: Transcript data
        
    Returns:
        SRT formatted string
    """
    segments = transcript.get("segments", [])
    srt_lines = []
    
    for i, segment in enumerate(segments, 1):
        start_time = format_timestamp(segment.get("start", 0))
        end_time = format_timestamp(segment.get("end", 0))
        text = segment.get("text", "")
        
        srt_lines.append(f"{i}\n{start_time} --> {end_time}\n{text}\n")
    
    # Join and ensure proper SRT format with trailing newline
    return "\n".join(srt_lines)


def convert_to_markdown(transcript: Dict[str, Any]) -> str:
    """Convert transcript to Markdown format with proper formatting.
    
    Args:
        transcript: Transcript data
        
    Returns:
        Markdown formatted string
    """
    md_lines = []
    
    # Add title and metadata
    title = transcript.get("title", "Transcript")
    md_lines.append(f"# {title}\n")
    
    # Add metadata section
    md_lines.append("## Metadata\n")
    created_at = transcript.get("created_at", "")
    duration = transcript.get("content", {}).get("duration", 0)
    md_lines.append(f"- **Created:** {created_at}")
    md_lines.append(f"- **Duration:** {format_duration(duration)}\n")
    
    # Add content section
    md_lines.append("## Content\n")
    
    # Process segments with speaker information and timestamps
    segments = transcript.get("segments", [])
    current_speaker = None
    
    for segment in segments:
        speaker = segment.get("speaker")
        start_time = format_duration(segment.get("start", 0))
        text = segment.get("text", "")
        
        # Add speaker change
        if speaker and speaker != current_speaker:
            current_speaker = speaker
            md_lines.append(f"### Speaker: {speaker}\n")
        
        # Add segment with timestamp
        md_lines.append(f"**[{start_time}]** {text}\n")
    
    return "\n".join(md_lines)
