"""
Text-to-Speech Component - Компонент синтеза речи
"""

import sys
from pathlib import Path
from typing import Dict, Any, Optional
from ..core import FrameworkComponent, ProcessingContext

# Импорт существующих модулей
project_root = Path(__file__).parent.parent.parent
src_path = project_root / "src"
sys.path.insert(0, str(src_path))

try:
    from core.speech_synthesizer import SpeechSynthesizer
    from core.tts_engine_factory import TTSEngineFactory
    TTS_AVAILABLE = True
except ImportError as e:
    print(f"Warning: TTS modules not available: {e}")
    TTS_AVAILABLE = False

class SpeechSynthesisComponent(FrameworkComponent):
    """Компонент синтеза речи"""

    def __init__(self, name: str = "speech_synthesis", config: Dict[str, Any] = None):
        super().__init__(name, config)
        self.speech_synthesizer = None
        self.tts_factory = None
        self.engine = self.config.get('engine', 'auto')

    def initialize(self) -> bool:
        """Инициализация компонента"""
        if not super().initialize():
            return False

        if not TTS_AVAILABLE:
            self.logger.error("TTS modules not available")
            return False

        try:
            self.speech_synthesizer = SpeechSynthesizer()
            self.tts_factory = TTSEngineFactory()
            self.logger.info(f"Initialized TTS with engine: {self.engine}")
            return True
        except Exception as e:
            self.logger.error(f"Failed to initialize TTS: {e}")
            return False

    def validate_config(self) -> bool:
        """Валидация конфигурации"""
        if not super().validate_config():
            return False

        engine = self.config.get('engine', 'auto')
        valid_engines = ['auto', 'google', 'elevenlabs', 'pyttsx3', 'edge']

        if engine not in valid_engines:
            self.logger.error(f"Invalid TTS engine: {engine}. Valid options: {valid_engines}")
            return False

        return True

    def process(self, context: ProcessingContext) -> ProcessingContext:
        """Обработка: синтез речи из переведенного текста"""
        if not self.speech_synthesizer or not self.tts_factory:
            raise RuntimeError("TTS components not initialized")

        # Получаем переведенный текст
        text_to_synthesize = context.translation
        if not text_to_synthesize:
            raise ValueError("No translated text found for synthesis")

        target_language = context.target_language
        self.logger.info(f"Synthesizing speech: {len(text_to_synthesize)} characters")
        self.logger.info(f"Target language: {target_language}")

        try:
            # Создаем выходной аудио файл
            audio_output = context.output_file.parent / f"{context.output_file.stem}_synthesized_audio.wav"

            # Колбэк для обновления прогресса
            def progress_callback(stage, progress):
                if context.progress_callback:
                    context.progress_callback(f"Speech Synthesis: {stage}", progress)

            # Настройки синтеза
            synthesis_settings = {
                'engine': self.engine,
                'language': target_language,
                'voice': self.config.get('voice_selection', 'auto'),
                'speed': self.config.get('speed', 1.0),
                'pitch': self.config.get('pitch', 1.0),
                'volume': self.config.get('volume', 1.0),
                'quality': self.config.get('quality', 'medium')
            }

            # API ключи если есть
            if self.config.get('google_credentials'):
                synthesis_settings['google_credentials'] = self.config['google_credentials']
            if self.config.get('elevenlabs_api_key'):
                synthesis_settings['elevenlabs_api_key'] = self.config['elevenlabs_api_key']

            # Выполняем синтез
            if context.progress_callback:
                context.progress_callback("Speech Synthesis: Preparing", 10)

            # Если есть временные метки, синтезируем по сегментам для правильной синхронизации
            if context.timestamps and self.config.get('use_timestamps', True):
                self.logger.info(f"Synthesizing with timestamps: {len(context.timestamps)} segments")
                audio_segments = self._synthesize_segments_separately(
                    context, synthesis_settings, progress_callback
                )
                # Сохраняем информацию о сегментах для video_assembly
                context.metadata['audio_segments'] = audio_segments
            else:
                # Обычный синтез всего текста
                self._synthesize_full_text(
                    text_to_synthesize, audio_output, synthesis_settings, progress_callback
                )

            if not audio_output.exists():
                raise RuntimeError("Speech synthesis failed - no audio file created")

            # Сохраняем путь к синтезированному аудио
            context.metadata['synthesized_audio_file'] = str(audio_output)

            # Метаданные синтеза
            context.metadata['speech_synthesis'] = {
                'engine_used': self.engine,
                'settings': synthesis_settings,
                'output_file': str(audio_output),
                'audio_duration': self._get_audio_duration(audio_output)
            }

            self.logger.info(f"Speech synthesis completed: {audio_output}")

        except Exception as e:
            self.logger.error(f"Speech synthesis failed: {e}")
            raise

        return context

    def _synthesize_full_text(self, text: str, output_file: Path, settings: Dict[str, Any], progress_callback):
        """Синтез полного текста"""
        try:
            # Используем speech_synthesizer для синтеза
            result_path = self.speech_synthesizer.synthesize_speech(
                text=text,
                language=settings.get('language', 'ru'),
                voice=settings.get('voice', 'auto'),
                speed=settings.get('speed', 1.0),
                pitch=settings.get('pitch', 0.0)
            )

            if not result_path:
                raise RuntimeError("TTS synthesis returned no result")

            # Перемещаем файл в нужное место если нужно
            if str(result_path) != str(output_file):
                import shutil
                shutil.move(result_path, output_file)

        except Exception as e:
            self.logger.error(f"Full text synthesis failed: {e}")
            raise

    def _synthesize_segments_separately(self, context: ProcessingContext,
                                       synthesis_settings: Dict[str, Any], progress_callback):
        """Синтез отдельных аудио файлов для каждого сегмента (как в оригинале)"""
        try:
            timestamps = context.timestamps
            if not timestamps:
                return []

            audio_segments = []
            total_segments = len(timestamps)

            for i, timestamp in enumerate(timestamps):
                segment_text = timestamp.get('translated_text', timestamp.get('text', ''))
                if not segment_text or not segment_text.strip():
                    # Создаем "пустой" сегмент для пауз
                    segment = {
                        'start_time': timestamp.get('start', 0),
                        'end_time': timestamp.get('end', 0),
                        'text': '',
                        'translated_text': '',
                        'translated_audio_path': None,
                        'success': False,
                        'status': 'empty',
                        'vad_is_speech': False,
                        'segment_id': i
                    }
                    audio_segments.append(segment)
                    continue

                # Прогресс
                segment_progress = 20 + (i / total_segments) * 70  # 20% базовый + 70% на синтез
                if progress_callback:
                    progress_callback(f"Synthesizing segment {i+1}/{total_segments}", segment_progress)

                # Создаем уникальный файл для сегмента
                segment_file = context.output_file.parent / f"segment_{i:03d}_{timestamp.get('start', 0):.1f}s.wav"

                # Синтез сегмента
                result_path = self.speech_synthesizer.synthesize_speech(
                    text=segment_text,
                    language=synthesis_settings.get('language', 'ru'),
                    voice=synthesis_settings.get('voice', 'auto'),
                    speed=synthesis_settings.get('speed', 1.0),
                    pitch=synthesis_settings.get('pitch', 0.0)
                )

                # Перемещаем файл в нужное место если нужно
                if result_path and str(result_path) != str(segment_file):
                    import shutil
                    shutil.move(result_path, segment_file)

                # Создаем сегмент в формате оригинального video_processor
                segment = {
                    'start_time': timestamp.get('start', 0),
                    'end_time': timestamp.get('end', 0),
                    'text': timestamp.get('text', ''),
                    'translated_text': segment_text,
                    'translated_audio_path': str(segment_file) if segment_file.exists() else None,
                    'success': segment_file.exists(),
                    'status': 'completed' if segment_file.exists() else 'failed',
                    'vad_is_speech': True,  # Предполагаем что есть речь
                    'segment_id': i
                }
                audio_segments.append(segment)

                self.logger.info(f"Segment {i}: {segment['start_time']:.1f}-{segment['end_time']:.1f}s -> {segment_file}")

            if progress_callback:
                progress_callback("Speech Synthesis: Completed", 100)

            self.logger.info(f"Created {len(audio_segments)} audio segments for video assembly")
            return audio_segments

        except Exception as e:
            self.logger.error(f"Segment synthesis failed: {e}")
            raise

    def _synthesize_with_timestamps(self, context: ProcessingContext, output_file: Path,
                                   settings: Dict[str, Any], progress_callback):
        """Синтез с учетом временных меток"""
        try:
            timestamps = context.timestamps
            if not timestamps:
                # Fallback к обычному синтезу
                self._synthesize_full_text(context.translation, output_file, settings, progress_callback)
                return

            # Создаем временные аудио файлы для каждого сегмента
            temp_audio_files = []
            total_segments = len(timestamps)

            for i, timestamp in enumerate(timestamps):
                segment_text = timestamp.get('translated_text', '')
                if not segment_text:
                    continue

                # Временный файл для сегмента
                temp_file = output_file.parent / f"temp_segment_{i:03d}.wav"
                temp_audio_files.append((temp_file, timestamp))

                # Прогресс
                segment_progress = (i / total_segments) * 80  # 80% на синтез сегментов
                if progress_callback:
                    progress_callback(f"Synthesizing segment {i+1}/{total_segments}", segment_progress)

                # Синтез сегмента
                result_path = self.speech_synthesizer.synthesize_speech(
                    text=segment_text,
                    language=settings.get('language', 'ru'),
                    voice=settings.get('voice', 'auto'),
                    speed=settings.get('speed', 1.0),
                    pitch=settings.get('pitch', 0.0)
                )

                # Перемещаем файл в нужное место если нужно
                if result_path and str(result_path) != str(temp_file):
                    import shutil
                    shutil.move(result_path, temp_file)

            # Объединяем аудио сегменты с учетом временных меток
            if progress_callback:
                progress_callback("Combining audio segments", 90)

            self._combine_audio_segments(temp_audio_files, output_file, settings)

            # Удаляем временные файлы
            for temp_file, _ in temp_audio_files:
                if temp_file.exists():
                    temp_file.unlink()

        except Exception as e:
            self.logger.error(f"Timestamp-based synthesis failed: {e}")
            # Fallback к обычному синтезу
            self._synthesize_full_text(context.translation, output_file, settings, progress_callback)

    def _combine_audio_segments(self, temp_files: list, output_file: Path, settings: Dict[str, Any]):
        """Объединение аудио сегментов с учетом временных меток"""
        try:
            # Простое объединение файлов (можно улучшить с учетом точных временных меток)
            import subprocess

            # Создаем список файлов для concat
            concat_list = []
            for temp_file, timestamp in temp_files:
                if temp_file.exists():
                    concat_list.append(f"file '{temp_file}'")

            if not concat_list:
                raise RuntimeError("No audio segments to combine")

            # Создаем файл со списком для ffmpeg
            concat_file = output_file.parent / "concat_list.txt"
            with open(concat_file, 'w') as f:
                f.write('\n'.join(concat_list))

            # Объединяем с помощью ffmpeg
            cmd = [
                'ffmpeg', '-y', '-f', 'concat', '-safe', '0',
                '-i', str(concat_file), '-c', 'copy', str(output_file)
            ]

            result = subprocess.run(cmd, capture_output=True, text=True)

            # Удаляем временный файл списка
            if concat_file.exists():
                concat_file.unlink()

            if result.returncode != 0:
                raise RuntimeError(f"ffmpeg failed: {result.stderr}")

        except Exception as e:
            self.logger.warning(f"Advanced audio combining failed: {e}, using simple concatenation")
            # Простое объединение без временных меток
            self._simple_audio_concat(temp_files, output_file)

    def _simple_audio_concat(self, temp_files: list, output_file: Path):
        """Простое объединение аудио файлов"""
        try:
            import wave

            # Объединяем WAV файлы
            output_wav = wave.open(str(output_file), 'wb')
            first_file = True

            for temp_file, _ in temp_files:
                if not temp_file.exists():
                    continue

                input_wav = wave.open(str(temp_file), 'rb')

                if first_file:
                    output_wav.setparams(input_wav.getparams())
                    first_file = False

                output_wav.writeframes(input_wav.readframes(input_wav.getnframes()))
                input_wav.close()

            output_wav.close()

        except Exception as e:
            self.logger.error(f"Simple audio concatenation failed: {e}")
            raise

    def _get_audio_duration(self, audio_file: Path) -> float:
        """Получение длительности аудио файла"""
        try:
            import wave
            with wave.open(str(audio_file), 'rb') as wav_file:
                frames = wav_file.getnframes()
                sample_rate = wav_file.getframerate()
                duration = frames / float(sample_rate)
                return duration
        except Exception:
            return 0.0

    def get_available_engines(self) -> Dict[str, bool]:
        """Получение доступных TTS движков"""
        if not self.tts_factory:
            return {}

        try:
            return self.tts_factory.get_available_engines()
        except Exception as e:
            self.logger.warning(f"Failed to get available engines: {e}")
            return {}

    def get_available_voices(self, engine: str = None, language: str = None) -> list:
        """Получение доступных голосов"""
        engine = engine or self.engine

        # Базовые голоса для разных движков
        voices = {
            'google': {
                'ru': ['ru-RU-Wavenet-A', 'ru-RU-Wavenet-B', 'ru-RU-Wavenet-C', 'ru-RU-Wavenet-D'],
                'en': ['en-US-Wavenet-A', 'en-US-Wavenet-B', 'en-US-Wavenet-C', 'en-US-Wavenet-D'],
                'es': ['es-ES-Wavenet-A', 'es-ES-Wavenet-B'],
                'fr': ['fr-FR-Wavenet-A', 'fr-FR-Wavenet-B'],
                'de': ['de-DE-Wavenet-A', 'de-DE-Wavenet-B']
            },
            'pyttsx3': ['auto', 'male', 'female'],
            'elevenlabs': ['auto', 'custom_voice_1', 'custom_voice_2']
        }

        if engine in voices:
            if language and language in voices[engine]:
                return voices[engine][language]
            elif isinstance(voices[engine], list):
                return voices[engine]
            else:
                # Возвращаем все голоса для всех языков
                all_voices = []
                for lang_voices in voices[engine].values():
                    all_voices.extend(lang_voices)
                return all_voices

        return ['auto', 'male', 'female']