import argparse
import asyncio
import json
import sys
from pathlib import Path

# Цвета для красивого вывода в консоль (опционально)
GREEN = "\033[92m"
RESET = "\033[0m"
RED = "\033[91m"
YELLOW = "\033[93m"


def parse_json_transcription(json_path):
    """
    Парсит JSON файл вида:
    {
        "scenes": [
            {
                "id": "scene-01",
                "caption_top": "ЗАШЁЛ НА СЕРВЕР\nЧИСТО ПОСТРОИТЬ МАШИНУ",
                "caption_bottom": "А ТЕБЯ УЖЕ\nПРИНУЖДАЮТ К БРАКУ",
                ...
            },
            ...
        ]
    }
    """
    entries = []

    print(f"Открываем JSON файл: {json_path}")
    with open(json_path, 'r', encoding='utf-8') as f:
        data = json.load(f)

    print(f"Ключи в JSON: {list(data.keys())}")

    if "scenes" not in data:
        print(f"{RED}Ошибка: в JSON не найден ключ 'scenes'{RESET}")
        print(f"Доступные ключи: {list(data.keys())}")
        return entries

    scenes = data["scenes"]
    print(f"Найдено сцен: {len(scenes)}")

    for i, scene in enumerate(scenes):
        scene_id = scene.get("id", f"scene-{i:02d}")
        print(f"\n{YELLOW}Обработка сцены {i + 1}/{len(scenes)}: {scene_id}{RESET}")
        print(f"  Доступные поля: {list(scene.keys())}")

        # Собираем текст из caption_top и caption_bottom
        texts = []

        if "caption_top" in scene and scene["caption_top"]:
            text_top = scene["caption_top"]
            print(f"  Найден caption_top: {text_top[:50]}...")
            texts.append(text_top)

        if "caption_bottom" in scene and scene["caption_bottom"]:
            text_bottom = scene["caption_bottom"]
            print(f"  Найден caption_bottom: {text_bottom[:50]}...")
            texts.append(text_bottom)

        # Если есть другие поля с текстом, можно добавить
        if "narration" in scene and scene["narration"]:
            texts.append(scene["narration"])
            print(f"  Найден narration: {scene['narration'][:50]}...")

        if not texts:
            print(f"  {RED}Предупреждение: в сцене {scene_id} нет текста для озвучки{RESET}")
            continue

        # Объединяем текст из всех полей, заменяем \n на пробелы
        combined_text = ". ".join(texts).replace("\n", " ").replace("\\n", " ")
        print(f"  Объединённый текст: {combined_text[:100]}...")

        # Определяем время
        if "selected_timestamp" in data.get("source", {}):
            # Используем таймкод из source
            time_str = data["source"]["selected_timestamp"]
            print(f"  Время из source.selected_timestamp: {time_str}")
            parts = time_str.split(':')
            if len(parts) == 3:
                hours, minutes, seconds = parts
                total_seconds = int(hours) * 3600 + int(minutes) * 60 + float(seconds)
            elif len(parts) == 2:
                minutes, seconds = parts
                total_seconds = int(minutes) * 60 + float(seconds)
            else:
                total_seconds = float(parts[0])
        elif "start_time" in scene:
            start_time_str = scene["start_time"]
            print(f"  Время из scene.start_time: {start_time_str}")
            # Парсим время в секундах
            parts = start_time_str.split(':')
            if len(parts) == 3:
                hours, minutes, seconds = parts
                total_seconds = int(hours) * 3600 + int(minutes) * 60 + float(seconds)
            elif len(parts) == 2:
                minutes, seconds = parts
                total_seconds = int(minutes) * 60 + float(seconds)
            else:
                total_seconds = float(parts[0])
        else:
            # Если нет времени, используем индекс сцены * 5 секунд
            total_seconds = i * 5
            print(f"  Время не найдено, используем индекс: {total_seconds}с")

        entries.append((total_seconds, combined_text, scene_id))
        print(f"  {GREEN}✓ Добавлена фраза для озвучки{RESET}")

    return entries


def group_sentences(entries, max_chars=250):
    """Группирует короткие фразы в более длинные для естественной озвучки."""
    grouped = []
    current_group = []
    current_text = ""
    current_start_time = None
    current_scene_ids = []

    for ts, text, scene_id in entries:
        if current_start_time is None:
            current_start_time = ts

        # Если добавить следующий текст, не превысит ли это лимит?
        if len(current_text) + len(text) + 2 <= max_chars:
            current_group.append((ts, text, scene_id))
            current_text += " " + text
            current_scene_ids.append(scene_id)
        else:
            # Текущая группа заполнена, сохраняем её
            if current_group:
                grouped.append((current_start_time, current_text.strip(), current_scene_ids.copy()))
            # Начинаем новую группу
            current_group = [(ts, text, scene_id)]
            current_text = text
            current_start_time = ts
            current_scene_ids = [scene_id]

    # Добавляем последнюю группу
    if current_group:
        grouped.append((current_start_time, current_text.strip(), current_scene_ids))
    return grouped


async def generate_audio(text, output_path, voice):
    """Генерирует аудио через edge-tts в отдельном процессе."""
    print(f"  Генерация для: {text[:50]}...")
    cmd = [
        "edge-tts",
        "--text", text,
        "--voice", voice,
        "--write-media", str(output_path)
    ]
    try:
        # Запускаем процесс и ждем его завершения
        process = await asyncio.create_subprocess_exec(
            *cmd,
            stdout=asyncio.subprocess.PIPE,
            stderr=asyncio.subprocess.PIPE
        )
        stdout, stderr = await process.communicate()
        if process.returncode != 0:
            print(f"    {RED}ОШИБКА edge-tts: {stderr.decode()}{RESET}", file=sys.stderr)
            return False
        return True
    except Exception as e:
        print(f"    {RED}ОШИБКА при запуске edge-tts: {e}{RESET}", file=sys.stderr)
        return False


async def main_async(json_path, output_dir, voice, max_chars):
    output_path = Path(output_dir)
    output_path.mkdir(parents=True, exist_ok=True)

    print(f"{YELLOW}Чтение JSON транскрипции: {json_path}{RESET}")
    entries = parse_json_transcription(json_path)

    if not entries:
        print(f"{RED}Ошибка: не найдено текста для озвучки в JSON файле.{RESET}")
        print("Проверьте, что в JSON есть поле 'scenes' и внутри него 'caption_top' или 'caption_bottom'")
        return

    print(f"\n{GREEN}Найдено {len(entries)} фраз для озвучки:{RESET}")
    for ts, text, scene_id in entries:
        print(f"  - [{ts:.2f}с] Сцена {scene_id}: {text[:50]}...")

    print(f"\n{YELLOW}Группировка фраз (макс. {max_chars} символов)...{RESET}")
    grouped_entries = group_sentences(entries, max_chars=max_chars)
    print(f"{GREEN}Сформировано {len(grouped_entries)} фрагментов для озвучки.{RESET}")

    manifest_path = output_path / "segments.txt"
    print(f"Манифест будет сохранен в: {manifest_path}")

    # Также сохраняем JSON манифест для удобства
    json_manifest = []

    with open(manifest_path, 'w', encoding='utf-8') as manifest:
        for i, (start_time, text, scene_ids) in enumerate(grouped_entries):
            audio_file = output_path / f"segment_{i:04d}.mp3"
            scene_ids_str = ",".join(scene_ids)
            print(f"\n[{i + 1}/{len(grouped_entries)}] Сцены: {scene_ids_str}, начало в {start_time:.2f}с")
            print(f"  Текст: {text[:100]}...")

            success = await generate_audio(text, audio_file, voice)
            if success:
                # Записываем в манифест: start_time путь_к_файлу scene_ids
                manifest.write(f"{start_time} {audio_file} [{scene_ids_str}]\n")
                json_manifest.append({
                    "segment_id": i,
                    "start_time": start_time,
                    "text": text,
                    "scene_ids": scene_ids,
                    "audio_file": str(audio_file)
                })
                print(f"{GREEN}  Успешно!{RESET}")
            else:
                print(f"  {RED}Не удалось сгенерировать фрагмент {i}{RESET}", file=sys.stderr)

    # Сохраняем JSON манифест
    json_manifest_path = output_path / "segments.json"
    with open(json_manifest_path, 'w', encoding='utf-8') as f:
        json.dump(json_manifest, f, ensure_ascii=False, indent=2)

    print(f"\n{'=' * 50}")
    print(f"{GREEN}Готово!{RESET}")
    print(f"  Текстовый манифест: {manifest_path}")
    print(f"  JSON манифест: {json_manifest_path}")
    print(f"  Аудио фрагменты: {output_path}/segment_*.mp3")

    # Выводим содержимое JSON манифеста для проверки
    if json_manifest:
        print(f"\n{GREEN}✅ JSON манифест содержит {len(json_manifest)} записей{RESET}")
        print(f"\nПример содержимого segments.json:")
        print(json.dumps(json_manifest[0] if json_manifest else [], ensure_ascii=False, indent=2))
    else:
        print(f"\n{RED}⚠️ ВНИМАНИЕ: JSON манифест пуст!{RESET}")
        print("Возможные причины:")
        print("  1. В JSON файле нет поля 'scenes'")
        print("  2. В сценах нет полей 'caption_top' или 'caption_bottom'")
        print("  3. Поля 'caption_top' или 'caption_bottom' пустые")
        print("  4. Ошибка при генерации аудио (проверьте установку edge-tts)")


def main():
    parser = argparse.ArgumentParser(description="Генерация озвучки из JSON файла (формат видео-мемов)")
    parser.add_argument("json_file", default="assets/scenario.json", nargs="?",
                        help="Путь к JSON файлу (по умолчанию: assets/scenario.json)")
    parser.add_argument("-o", "--output-dir", default="voice/segments",
                        help="Директория для сохранения аудиофрагментов (по умолчанию: voice/segments)")
    parser.add_argument("-v", "--voice", default="ru-RU-SvetlanaNeural",
                        help="Голос edge-tts (по умолчанию: ru-RU-SvetlanaNeural). Дмитрий: ru-RU-DmitryNeural")
    parser.add_argument("-g", "--group-len", type=int, default=250,
                        help="Максимальная длина текста для одного фрагмента (по умолчанию: 250 символов)")
    args = parser.parse_args()

    # Запускаем асинхронную функцию
    asyncio.run(main_async(args.json_file, args.output_dir, args.voice, args.group_len))


if __name__ == "__main__":
    main()