"""
🎬 Celery задачи для django-hlsfield

Этот модуль содержит все Celery задачи для асинхронной обработки видео:
- HLS/DASH транскодинг
- Прогрессивная обработка
- Оптимизация и аналитика
- Batch операции

Автор: akula993
Лицензия: MIT
"""

from __future__ import annotations

import logging
import os
import time
from pathlib import Path
from typing import Dict, List, Optional, Any

try:
    from celery import shared_task, group, chain
    from celery.exceptions import Retry

    CELERY_AVAILABLE = True
except ImportError:
    # Заглушки для работы без Celery
    def shared_task(*args, **kwargs):
        def decorator(func):
            func.delay = lambda *args, **kwargs: func(*args, **kwargs)
            return func

        return decorator


    def group(*args):
        return None


    def chain(*args):
        return None


    class Retry(Exception):
        pass


    CELERY_AVAILABLE = False

from django.apps import apps
from django.db import transaction
from django.utils import timezone
from django.core.cache import cache

from . import utils, defaults
from .exceptions import (
    TranscodingError,
    FFmpegError,
    InvalidVideoError,
    StorageError
)

logger = logging.getLogger(__name__)


# ==============================================================================
# ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ
# ==============================================================================

def _resolve_field(instance, field_name: str):
    """Получает информацию о поле и связанных объектах"""
    field = instance._meta.get_field(field_name)
    file = getattr(instance, field_name)
    storage = file.storage
    name = file.name
    return field, file, storage, name


def _get_base_key(name: str, subdir: str) -> str:
    """Генерирует базовый ключ для выходных файлов"""
    base, _ext = os.path.splitext(name)
    return f"{base}/{subdir}/"


def _update_instance_status(instance, status: str, **extra_fields):
    """Безопасно обновляет статус обработки"""
    try:
        update_fields = []

        # Статус обработки
        if hasattr(instance, 'processing_status'):
            setattr(instance, 'processing_status', status)
            update_fields.append('processing_status')

        # Дополнительные поля
        for field_name, value in extra_fields.items():
            if hasattr(instance, field_name):
                setattr(instance, field_name, value)
                update_fields.append(field_name)

        if update_fields:
            with transaction.atomic():
                instance.save(update_fields=update_fields)

    except Exception as e:
        logger.warning(f"Could not update instance status: {e}")


def _handle_task_error(instance, error: Exception, task_name: str):
    """Обрабатывает ошибки задач"""
    error_msg = f"{task_name} failed: {str(error)[:100]}"
    logger.error(f"Task {task_name} failed for {instance}: {error}")

    _update_instance_status(
        instance,
        f"error_{task_name}",
        error_message=error_msg
    )

    # Отправляем уведомление если настроено
    try:
        from django.core.mail import mail_admins
        mail_admins(
            f"Video processing error: {task_name}",
            f"Error processing {instance}: {error}",
            fail_silently=True
        )
    except:
        pass


# ==============================================================================
# ОСНОВНЫЕ ЗАДАЧИ ТРАНСКОДИНГА
# ==============================================================================

@shared_task(bind=True, max_retries=3, default_retry_delay=60)
def build_hls_for_field(self, model_label: str, pk: int | str, field_name: str):
    """Асинхронная задача создания HLS стрима"""
    try:
        return build_hls_for_field_sync(model_label, pk, field_name)
    except Exception as exc:
        logger.error(f"HLS task failed (attempt {self.request.retries + 1}): {exc}")

        # Retry на временные ошибки
        if self.request.retries < self.max_retries:
            if isinstance(exc, (StorageError, TranscodingError)):
                raise self.retry(countdown=60 * (self.request.retries + 1))

        # Финальная ошибка
        Model = apps.get_model(model_label)
        instance = Model.objects.get(pk=pk)
        _handle_task_error(instance, exc, "HLS")
        raise


def build_hls_for_field_sync(model_label: str, pk: int | str, field_name: str):
    """Синхронная версия создания HLS"""
    logger.info(f"Starting HLS transcoding for {model_label}:{pk}.{field_name}")

    Model = apps.get_model(model_label)
    instance = Model.objects.get(pk=pk)
    field, file, storage, name = _resolve_field(instance, field_name)

    _update_instance_status(instance, "transcoding_hls")

    start_time = time.time()

    try:
        with utils.tempdir(prefix=f"hls_{pk}_") as td:
            # Загружаем исходник локально
            logger.debug("Pulling video to local storage...")
            local_input = utils.pull_to_local(storage, name, td)

            # Создаем HLS структуру
            local_hls_root = td / "hls_out"
            local_hls_root.mkdir(parents=True, exist_ok=True)

            # Получаем параметры обработки
            ladder = getattr(field, "ladder", defaults.DEFAULT_LADDER)
            segment_duration = getattr(field, "segment_duration", defaults.SEGMENT_DURATION)

            logger.info(f"Transcoding {len(ladder)} HLS variants, {segment_duration}s segments")

            # Выполняем транскодинг
            master = utils.transcode_hls_variants(
                input_path=local_input,
                out_dir=local_hls_root,
                ladder=ladder,
                segment_duration=segment_duration,
            )

            # Загружаем результат в storage
            base_key = _get_base_key(name, getattr(field, "hls_base_subdir", defaults.HLS_SUBDIR))
            utils.save_tree_to_storage(local_hls_root, storage, base_key)

            master_key = base_key + master.name

        # Обновляем модель
        playlist_field = getattr(field, "hls_playlist_field", None)
        update_fields = {}

        if playlist_field:
            setattr(instance, playlist_field, master_key)
            update_fields[playlist_field] = master_key

        if hasattr(instance, "hls_built_at"):
            built_at = timezone.now()
            setattr(instance, "hls_built_at", built_at)
            update_fields["hls_built_at"] = built_at

        elapsed = time.time() - start_time
        logger.info(f"HLS transcoding completed in {elapsed:.1f}s")

        _update_instance_status(
            instance,
            "hls_ready",
            transcoding_time=int(elapsed),
            **update_fields
        )

        return {
            "status": "success",
            "master_playlist": master_key,
            "transcoding_time": elapsed,
            "variants": len(ladder)
        }

    except Exception as e:
        _handle_task_error(instance, e, "HLS")
        raise


@shared_task(bind=True, max_retries=3, default_retry_delay=60)
def build_dash_for_field(self, model_label: str, pk: int | str, field_name: str):
    """Асинхронная задача создания DASH стрима"""
    try:
        return build_dash_for_field_sync(model_label, pk, field_name)
    except Exception as exc:
        logger.error(f"DASH task failed (attempt {self.request.retries + 1}): {exc}")

        if self.request.retries < self.max_retries:
            if isinstance(exc, (StorageError, TranscodingError)):
                raise self.retry(countdown=60 * (self.request.retries + 1))

        Model = apps.get_model(model_label)
        instance = Model.objects.get(pk=pk)
        _handle_task_error(instance, exc, "DASH")
        raise


def build_dash_for_field_sync(model_label: str, pk: int | str, field_name: str):
    """Синхронная версия создания DASH"""
    logger.info(f"Starting DASH transcoding for {model_label}:{pk}.{field_name}")

    Model = apps.get_model(model_label)
    instance = Model.objects.get(pk=pk)
    field, file, storage, name = _resolve_field(instance, field_name)

    _update_instance_status(instance, "transcoding_dash")

    start_time = time.time()

    try:
        with utils.tempdir(prefix=f"dash_{pk}_") as td:
            local_input = utils.pull_to_local(storage, name, td)
            local_dash_root = td / "dash_out"
            local_dash_root.mkdir(parents=True, exist_ok=True)

            ladder = getattr(field, "ladder", defaults.DEFAULT_LADDER)
            segment_duration = getattr(field, "segment_duration", defaults.DASH_SEGMENT_DURATION)

            logger.info(f"Transcoding {len(ladder)} DASH representations")

            manifest = utils.transcode_dash_variants(
                input_path=local_input,
                out_dir=local_dash_root,
                ladder=ladder,
                segment_duration=segment_duration,
            )

            base_key = _get_base_key(name, getattr(field, "dash_base_subdir", "dash"))
            utils.save_tree_to_storage(local_dash_root, storage, base_key)

            manifest_key = base_key + manifest.name

        # Обновляем модель
        manifest_field = getattr(field, "dash_manifest_field", None)
        update_fields = {}

        if manifest_field:
            setattr(instance, manifest_field, manifest_key)
            update_fields[manifest_field] = manifest_key

        if hasattr(instance, "dash_built_at"):
            built_at = timezone.now()
            setattr(instance, "dash_built_at", built_at)
            update_fields["dash_built_at"] = built_at

        elapsed = time.time() - start_time
        logger.info(f"DASH transcoding completed in {elapsed:.1f}s")

        _update_instance_status(
            instance,
            "dash_ready",
            transcoding_time=int(elapsed),
            **update_fields
        )

        return {
            "status": "success",
            "manifest": manifest_key,
            "transcoding_time": elapsed,
            "representations": len(ladder)
        }

    except Exception as e:
        _handle_task_error(instance, e, "DASH")
        raise


@shared_task(bind=True, max_retries=2, default_retry_delay=120)
def build_adaptive_for_field(self, model_label: str, pk: int | str, field_name: str):
    """Асинхронная задача создания HLS + DASH"""
    try:
        return build_adaptive_for_field_sync(model_label, pk, field_name)
    except Exception as exc:
        logger.error(f"Adaptive task failed (attempt {self.request.retries + 1}): {exc}")

        if self.request.retries < self.max_retries:
            if isinstance(exc, (StorageError, TranscodingError)):
                raise self.retry(countdown=120 * (self.request.retries + 1))

        Model = apps.get_model(model_label)
        instance = Model.objects.get(pk=pk)
        _handle_task_error(instance, exc, "Adaptive")
        raise


def build_adaptive_for_field_sync(model_label: str, pk: int | str, field_name: str):
    """Синхронная версия создания HLS + DASH"""
    logger.info(f"Starting adaptive transcoding for {model_label}:{pk}.{field_name}")

    Model = apps.get_model(model_label)
    instance = Model.objects.get(pk=pk)
    field, file, storage, name = _resolve_field(instance, field_name)

    _update_instance_status(instance, "transcoding_adaptive")

    start_time = time.time()

    try:
        with utils.tempdir(prefix=f"adaptive_{pk}_") as td:
            local_input = utils.pull_to_local(storage, name, td)
            local_adaptive_root = td / "adaptive_out"
            local_adaptive_root.mkdir(parents=True, exist_ok=True)

            ladder = getattr(field, "ladder", defaults.DEFAULT_LADDER)
            segment_duration = getattr(field, "segment_duration", defaults.SEGMENT_DURATION)

            logger.info(f"Creating HLS + DASH with {len(ladder)} qualities")

            results = utils.transcode_adaptive_variants(
                input_path=local_input,
                out_dir=local_adaptive_root,
                ladder=ladder,
                segment_duration=segment_duration,
            )

            base_key = _get_base_key(name, getattr(field, "adaptive_base_subdir", "adaptive"))
            utils.save_tree_to_storage(local_adaptive_root, storage, base_key)

            hls_master_key = base_key + f"hls/{results['hls_master'].name}"
            dash_manifest_key = base_key + f"dash/{results['dash_manifest'].name}"

        # Обновляем модель
        hls_field = getattr(field, "hls_playlist_field", None)
        dash_field = getattr(field, "dash_manifest_field", None)
        update_fields = {}

        if hls_field:
            setattr(instance, hls_field, hls_master_key)
            update_fields[hls_field] = hls_master_key

        if dash_field:
            setattr(instance, dash_field, dash_manifest_key)
            update_fields[dash_field] = dash_manifest_key

        if hasattr(instance, "adaptive_built_at"):
            built_at = timezone.now()
            setattr(instance, "adaptive_built_at", built_at)
            update_fields["adaptive_built_at"] = built_at

        elapsed = time.time() - start_time
        logger.info(f"Adaptive transcoding completed in {elapsed:.1f}s")

        _update_instance_status(
            instance,
            "adaptive_ready",
            transcoding_time=int(elapsed),
            **update_fields
        )

        return {
            "status": "success",
            "hls_master": hls_master_key,
            "dash_manifest": dash_manifest_key,
            "transcoding_time": elapsed,
            "variants": len(ladder)
        }

    except Exception as e:
        _handle_task_error(instance, e, "Adaptive")
        raise


# ==============================================================================
# ПРОГРЕССИВНЫЕ ЗАДАЧИ
# ==============================================================================

@shared_task
def build_progressive_for_field(model_label: str, pk: int | str, field_name: str,
                                options: Dict[str, Any]):
    """Прогрессивное создание адаптивного видео"""
    return build_progressive_for_field_sync(model_label, pk, field_name, options)


def build_progressive_for_field_sync(model_label: str, pk: int | str, field_name: str,
                                     options: Dict[str, Any]):
    """Синхронная версия прогрессивной обработки"""
    logger.info(f"Starting progressive transcoding for {model_label}:{pk}")

    Model = apps.get_model(model_label)
    instance = Model.objects.get(pk=pk)
    field, file, storage, name = _resolve_field(instance, field_name)

    preview_first = options.get('preview_first', True)
    progressive_delay = options.get('progressive_delay', 60)
    priority_heights = options.get('priority_heights', [360, 720])

    ladder = getattr(field, 'ladder', defaults.DEFAULT_LADDER)

    # Сортируем лестницу: приоритетные сначала
    priority_ladder = [r for r in ladder if r['height'] in priority_heights]
    regular_ladder = [r for r in ladder if r['height'] not in priority_heights]

    ordered_ladder = (sorted(priority_ladder, key=lambda x: x['height']) +
                      sorted(regular_ladder, key=lambda x: x['height']))

    try:
        with utils.tempdir(prefix=f"progressive_{pk}_") as td:
            local_input = utils.pull_to_local(storage, name, td)

            if preview_first and ordered_ladder:
                # Создаем превью качество
                preview_quality = ordered_ladder[0]
                logger.info(f"Creating preview quality: {preview_quality['height']}p")

                _build_single_quality(
                    local_input, storage, name, [preview_quality],
                    field, instance, is_preview=True
                )

                remaining_ladder = ordered_ladder[1:]
            else:
                remaining_ladder = ordered_ladder

            # Создаем остальные качества с задержками
            for i, quality in enumerate(remaining_ladder):
                if i > 0 and progressive_delay > 0:
                    logger.info(f"Waiting {progressive_delay}s before next quality...")
                    time.sleep(progressive_delay)

                current_ladder = ordered_ladder[:len(ordered_ladder) - len(remaining_ladder) + i + 1]
                logger.info(f"Adding quality: {quality['height']}p ({len(current_ladder)} total)")

                _build_single_quality(
                    local_input, storage, name, current_ladder,
                    field, instance, is_preview=False
                )

        logger.info("Progressive transcoding completed")
        return {"status": "success", "total_qualities": len(ordered_ladder)}

    except Exception as e:
        _handle_task_error(instance, e, "Progressive")
        raise


def _build_single_quality(local_input: Path, storage, name: str,
                          current_ladder: List[Dict], field, instance,
                          is_preview: bool = False):
    """Создает текущую лестницу качеств и обновляет манифесты"""

    with utils.tempdir(prefix=f"quality_{len(current_ladder)}_") as td:
        adaptive_root = td / "adaptive_out"
        adaptive_root.mkdir(parents=True, exist_ok=True)

        segment_duration = getattr(field, 'segment_duration', defaults.SEGMENT_DURATION)

        results = utils.transcode_adaptive_variants(
            input_path=local_input,
            out_dir=adaptive_root,
            ladder=current_ladder,
            segment_duration=segment_duration,
        )

        # Загружаем в storage
        base_key = _get_base_key(name, getattr(field, "adaptive_base_subdir", "adaptive"))
        utils.save_tree_to_storage(adaptive_root, storage, base_key)

        hls_master_key = base_key + f"hls/{results['hls_master'].name}"
        dash_manifest_key = base_key + f"dash/{results['dash_manifest'].name}"

        # Обновляем модель
        hls_field = getattr(field, "hls_playlist_field", None)
        dash_field = getattr(field, "dash_manifest_field", None)
        update_fields = {}

        if hls_field:
            setattr(instance, hls_field, hls_master_key)
            update_fields[hls_field] = hls_master_key

        if dash_field:
            setattr(instance, dash_field, dash_manifest_key)
            update_fields[dash_field] = dash_manifest_key

        # Статус
        if is_preview:
            status = 'preview_ready'
        else:
            status = f'ready_{len(current_ladder)}_qualities'

        _update_instance_status(
            instance,
            status,
            qualities_ready=len(current_ladder),
            **update_fields
        )


# ==============================================================================
# BATCH ОПЕРАЦИИ
# ==============================================================================

@shared_task
def batch_optimize_videos(model_label: str, pks: List[int], field_name: str,
                          target_qualities: int = 3):
    """Массовая оптимизация видео"""
    logger.info(f"Starting batch optimization for {len(pks)} videos")

    results = []
    Model = apps.get_model(model_label)

    for pk in pks:
        try:
            instance = Model.objects.get(pk=pk)

            # Запускаем оптимизацию
            result = optimize_existing_video.delay(
                model_label, pk, field_name, target_qualities
            )

            results.append({
                "pk": pk,
                "status": "queued",
                "task_id": result.id
            })

        except Exception as e:
            logger.error(f"Failed to queue optimization for {pk}: {e}")
            results.append({
                "pk": pk,
                "status": "error",
                "message": str(e)
            })

    return {
        "total_processed": len(pks),
        "successful": len([r for r in results if r["status"] == "queued"]),
        "failed": len([r for r in results if r["status"] == "error"]),
        "results": results
    }


@shared_task
def optimize_existing_video(model_label: str, pk: int | str, field_name: str,
                            target_qualities: int = 5, max_file_size_mb: int = None):
    """Оптимизирует существующее видео"""
    logger.info(f"Optimizing video {model_label}:{pk}")

    Model = apps.get_model(model_label)
    instance = Model.objects.get(pk=pk)
    field, file, storage, name = _resolve_field(instance, field_name)

    try:
        with utils.tempdir(prefix=f"optimize_{pk}_") as td:
            local_input = utils.pull_to_local(storage, name, td)

            # Анализируем исходное видео
            analysis = utils.analyze_video_complexity(local_input)

            # Генерируем оптимальную лестницу
            from .fields import get_optimal_ladder_for_resolution
            info = utils.ffprobe_streams(local_input)
            video_stream, _ = utils.pick_video_audio_streams(info)

            if video_stream:
                width = int(video_stream.get('width', 1920))
                height = int(video_stream.get('height', 1080))
                new_ladder = get_optimal_ladder_for_resolution(width, height)

                # Ограничиваем количество качеств
                if len(new_ladder) > target_qualities:
                    step = len(new_ladder) // target_qualities
                    new_ladder = new_ladder[::step][:target_qualities]

                # Корректируем под ограничения размера
                if max_file_size_mb:
                    new_ladder = _adjust_ladder_for_size_limit(
                        new_ladder, analysis, max_file_size_mb
                    )

                # Создаем новые варианты
                _build_single_quality(local_input, storage, name, new_ladder, field, instance)

                logger.info(f"Video optimized with {len(new_ladder)} qualities")
                return {
                    "status": "success",
                    "optimized_qualities": len(new_ladder),
                    "estimated_size_reduction": "N/A"
                }

    except Exception as e:
        _handle_task_error(instance, e, "Optimization")
        raise


def _adjust_ladder_for_size_limit(ladder: List[Dict], analysis: Dict,
                                  max_size_mb: int) -> List[Dict]:
    """Корректирует битрейты под ограничение размера"""
    duration = analysis.get('duration', 0)
    if duration <= 0:
        return ladder

    # Рассчитываем текущий размер
    total_size = 0
    for quality in ladder:
        total_bitrate = (quality['v_bitrate'] + quality['a_bitrate']) * 1000
        size_mb = (total_bitrate * duration) / (8 * 1024 * 1024)
        total_size += size_mb

    if total_size <= max_size_mb:
        return ladder

    # Пропорционально уменьшаем битрейты
    reduction_factor = max_size_mb / total_size

    adjusted_ladder = []
    for quality in ladder:
        adjusted = quality.copy()
        adjusted['v_bitrate'] = max(200, int(quality['v_bitrate'] * reduction_factor))
        adjusted['a_bitrate'] = max(64, int(quality['a_bitrate'] * reduction_factor))
        adjusted_ladder.append(adjusted)

    return adjusted_ladder


@shared_task
def health_check_videos(model_label: str, field_name: str):
    """Проверяет целостность видео файлов"""
    logger.info(f"Running health check for {model_label}.{field_name}")

    Model = apps.get_model(model_label)
    issues = []
    checked_count = 0

    for instance in Model.objects.all():
        checked_count += 1
        field, file, storage, name = _resolve_field(instance, field_name)

        issue = {"pk": instance.pk, "problems": []}

        # Проверяем основной файл
        try:
            if not storage.exists(name):
                issue["problems"].append("Original file missing")
        except Exception as e:
            issue["problems"].append(f"Cannot check original file: {e}")

        # Проверяем манифесты
        hls_field = getattr(field, "hls_playlist_field", None)
        if hls_field:
            hls_path = getattr(instance, hls_field, None)
            if hls_path:
                try:
                    if not storage.exists(hls_path):
                        issue["problems"].append("HLS manifest missing")
                except Exception as e:
                    issue["problems"].append(f"Cannot check HLS manifest: {e}")

        dash_field = getattr(field, "dash_manifest_field", None)
        if dash_field:
            dash_path = getattr(instance, dash_field, None)
            if dash_path:
                try:
                    if not storage.exists(dash_path):
                        issue["problems"].append("DASH manifest missing")
                except Exception as e:
                    issue["problems"].append(f"Cannot check DASH manifest: {e}")

        if issue["problems"]:
            issues.append(issue)

    logger.info(f"Health check completed: {len(issues)} issues found")

    return {
        "total_checked": checked_count,
        "issues_found": len(issues),
        "issues": issues,
        "healthy_videos": checked_count - len(issues)
    }


# ==============================================================================
# УТИЛИТЫ И МОНИТОРИНГ
# ==============================================================================

@shared_task
def cleanup_old_temp_files():
    """Очищает старые временные файлы"""
    import glob
    import time

    temp_patterns = [
        '/tmp/hlsfield_*',
        '/tmp/hls_*',
        '/tmp/dash_*',
        '/tmp/adaptive_*',
    ]

    cleaned = 0
    errors = 0
    cutoff_time = time.time() - 86400  # 24 часа

    for pattern in temp_patterns:
        try:
            for path in glob.glob(pattern):
                path_obj = Path(path)
                if path_obj.stat().st_mtime < cutoff_time:
                    if path_obj.is_dir():
                        import shutil
                        shutil.rmtree(path, ignore_errors=True)
                    else:
                        path_obj.unlink(missing_ok=True)
                    cleaned += 1
        except Exception as e:
            logger.warning(f"Error cleaning temp files {pattern}: {e}")
            errors += 1

    logger.info(f"Cleaned {cleaned} old temp files, {errors} errors")
    return {"cleaned": cleaned, "errors": errors}


@shared_task
def generate_video_analytics_report(days: int = 7):
    """Генерирует отчет аналитики видео"""
    from django.utils import timezone
    from datetime import timedelta

    try:
        from .views import VideoEvent

        start_date = timezone.now() - timedelta(days=days)
        events = VideoEvent.objects.filter(timestamp__gte=start_date)

        report = {
            "period": f"Last {days} days",
            "total_events": events.count(),
            "unique_videos": events.values('video_id').distinct().count(),
            "unique_sessions": events.values('session_id').distinct().count(),
            "play_events": events.filter(event_type='play').count(),
            "completion_events": events.filter(event_type='ended').count(),
            "error_events": events.filter(event_type='error').count(),
            "buffer_events": events.filter(event_type='buffer_start').count(),
        }

        # Топ видео по просмотрам
        top_videos = events.filter(event_type='play').values('video_id').annotate(
            views=models.Count('id')
        ).order_by('-views')[:10]
        report['top_videos'] = list(top_videos)

        # Распределение по качеству
        quality_dist = events.exclude(quality__isnull=True).values('quality').annotate(
            count=models.Count('id')
        ).order_by('-count')
        report['quality_distribution'] = list(quality_dist)

        # Сохраняем в кеш
        cache.set(f'video_analytics_report_{days}d', report, 3600)  # 1 час

        logger.info(f"Generated analytics report for {days} days")
        return report

    except Exception as e:
        logger.error(f"Failed to generate analytics report: {e}")
        return {"error": str(e)}


@shared_task
def monitor_transcoding_performance():
    """Мониторинг производительности транскодинга"""

    # Получаем статистику из кеша или базы
    stats = {
        "average_hls_time": 0,
        "average_dash_time": 0,
        "success_rate": 0,
        "failed_tasks_24h": 0,
        "queue_length": 0
    }

    try:
        # Анализируем последние задачи
        if CELERY_AVAILABLE:
            from celery import current_app
            inspect = current_app.control.inspect()

            # Длина очереди
            active_tasks = inspect.active()
            if active_tasks:
                total_active = sum(len(tasks) for tasks in active_tasks.values())
                stats["queue_length"] = total_active

        # Статистика ошибок из логов или базы
        # Здесь можно добавить анализ логов или специальной таблицы

        cache.set('transcoding_performance_stats', stats, 300)  # 5 минут
        return stats

    except Exception as e:
        logger.error(f"Performance monitoring failed: {e}")
        return {"error": str(e)}


# ==============================================================================
# ЗАДАЧИ ОБСЛУЖИВАНИЯ
# ==============================================================================

@shared_task
def regenerate_missing_previews(model_label: str, field_name: str):
    """Восстанавливает отсутствующие превью"""
    logger.info(f"Regenerating missing previews for {model_label}.{field_name}")

    Model = apps.get_model(model_label)
    regenerated = 0
    errors = 0

    for instance in Model.objects.all():
        try:
            field, file, storage, name = _resolve_field(instance, field_name)

            # Проверяем есть ли превью
            has_preview = False

            # Проверяем поле модели
            preview_field = getattr(field, "preview_field", None)
            if preview_field:
                preview_path = getattr(instance, preview_field, None)
                if preview_path and storage.exists(preview_path):
                    has_preview = True

            # Проверяем sidecar файл
            if not has_preview:
                try:
                    preview_url = file.preview_url()
                    if preview_url:
                        has_preview = True
                except:
                    pass

            if not has_preview:
                # Создаем превью
                with utils.tempdir(prefix=f"preview_{instance.pk}_") as td:
                    local_input = utils.pull_to_local(storage, name, td)
                    preview_tmp = td / "preview.jpg"

                    utils.extract_preview(
                        local_input,
                        preview_tmp,
                        at_sec=getattr(field, "preview_at", 3.0)
                    )

                    if preview_tmp.exists():
                        # Сохраняем превью
                        if field.sidecar_layout == "nested":
                            preview_key = f"{file._base_key()}/{field.preview_filename}"
                        else:
                            preview_key = f"{file._base_key()}_preview.jpg"

                        with preview_tmp.open("rb") as fh:
                            saved_path = storage.save(preview_key, fh)

                        # Обновляем модель
                        if preview_field:
                            setattr(instance, preview_field, saved_path)
                            instance.save(update_fields=[preview_field])

                        regenerated += 1
                        logger.info(f"Regenerated preview for {instance.pk}")

        except Exception as e:
            logger.error(f"Failed to regenerate preview for {instance.pk}: {e}")
            errors += 1

    return {
        "regenerated": regenerated,
        "errors": errors,
        "total_processed": regenerated + errors
    }


@shared_task
def update_video_statistics():
    """Обновляет статистику видео"""

    try:
        from django.db.models import Count, Avg, Sum
        from .views import VideoEvent

        # Группируем статистику по видео
        video_stats = VideoEvent.objects.values('video_id').annotate(
            total_views=Count('id', filter=models.Q(event_type='play')),
            unique_viewers=Count('session_id', distinct=True),
            avg_watch_time=Avg('current_time', filter=models.Q(event_type__in=['ended', 'pause'])),
            completion_rate=Count('id', filter=models.Q(event_type='ended')) * 100.0 / Count('id', filter=models.Q(
                event_type='play'))
        )

        updated = 0

        for stat in video_stats:
            video_id = stat['video_id']

            # Сохраняем в кеш для быстрого доступа
            cache_key = f"video_stats_{video_id}"
            cache.set(cache_key, {
                'total_views': stat['total_views'] or 0,
                'unique_viewers': stat['unique_viewers'] or 0,
                'avg_watch_time': stat['avg_watch_time'] or 0,
                'completion_rate': stat['completion_rate'] or 0,
            }, 3600)  # 1 час

            updated += 1

        logger.info(f"Updated statistics for {updated} videos")
        return {"updated": updated}

    except Exception as e:
        logger.error(f"Failed to update video statistics: {e}")
        return {"error": str(e)}


# ==============================================================================
# ЗАДАЧИ МИГРАЦИИ И ОБСЛУЖИВАНИЯ
# ==============================================================================

@shared_task
def migrate_old_video_format(model_label: str, field_name: str):
    """Мигрирует видео из старого формата в новый"""
    logger.info(f"Starting video format migration for {model_label}.{field_name}")

    Model = apps.get_model(model_label)
    migrated = 0
    errors = 0

    # Находим видео которые нужно мигрировать
    instances = Model.objects.filter(
        # Условие что есть исходное видео но нет HLS/DASH
        **{f"{field_name}__isnull": False}
    )

    hls_field_name = None
    dash_field_name = None

    # Определяем поля для HLS/DASH
    field = instances.first()._meta.get_field(field_name) if instances.exists() else None
    if field:
        hls_field_name = getattr(field, "hls_playlist_field", None)
        dash_field_name = getattr(field, "dash_manifest_field", None)

    if hls_field_name:
        instances = instances.filter(**{f"{hls_field_name}__isnull": True})

    logger.info(f"Found {instances.count()} videos to migrate")

    for instance in instances:
        try:
            # Запускаем транскодинг для миграции
            if hasattr(field, 'dash_manifest_field') and hasattr(field, 'hls_playlist_field'):
                # Адаптивное поле
                build_adaptive_for_field.delay(model_label, instance.pk, field_name)
            elif hls_field_name:
                # HLS поле
                build_hls_for_field.delay(model_label, instance.pk, field_name)
            elif dash_field_name:
                # DASH поле
                build_dash_for_field.delay(model_label, instance.pk, field_name)

            migrated += 1

        except Exception as e:
            logger.error(f"Failed to queue migration for {instance.pk}: {e}")
            errors += 1

    return {
        "queued_for_migration": migrated,
        "errors": errors,
        "total_found": migrated + errors
    }


@shared_task
def validate_video_integrity(model_label: str, field_name: str, repair: bool = False):
    """Валидирует целостность видео файлов и при необходимости восстанавливает"""
    logger.info(f"Validating video integrity for {model_label}.{field_name}, repair={repair}")

    Model = apps.get_model(model_label)
    results = {
        "checked": 0,
        "valid": 0,
        "corrupted": 0,
        "missing": 0,
        "repaired": 0,
        "repair_failed": 0
    }

    for instance in Model.objects.all():
        results["checked"] += 1

        try:
            field, file, storage, name = _resolve_field(instance, field_name)

            # Проверяем существование
            if not storage.exists(name):
                results["missing"] += 1
                logger.warning(f"Missing video file for {instance.pk}: {name}")
                continue

            # Валидируем через ffprobe
            with utils.tempdir(prefix=f"validate_{instance.pk}_") as td:
                try:
                    local_file = utils.pull_to_local(storage, name, td)
                    validation = utils.validate_video_file(local_file)

                    if validation['valid']:
                        results["valid"] += 1
                    else:
                        results["corrupted"] += 1
                        logger.warning(f"Corrupted video {instance.pk}: {validation['issues']}")

                        if repair:
                            # Пытаемся восстановить через повторный транскодинг
                            try:
                                _update_instance_status(instance, "repairing")

                                # Определяем тип поля и запускаем соответствующую задачу
                                if hasattr(field, 'adaptive_base_subdir'):
                                    build_adaptive_for_field.delay(model_label, instance.pk, field_name)
                                elif hasattr(field, 'hls_playlist_field'):
                                    build_hls_for_field.delay(model_label, instance.pk, field_name)
                                elif hasattr(field, 'dash_manifest_field'):
                                    build_dash_for_field.delay(model_label, instance.pk, field_name)

                                results["repaired"] += 1
                                logger.info(f"Queued repair for {instance.pk}")

                            except Exception as repair_error:
                                results["repair_failed"] += 1
                                logger.error(f"Failed to queue repair for {instance.pk}: {repair_error}")

                except Exception as validation_error:
                    logger.error(f"Validation failed for {instance.pk}: {validation_error}")
                    results["corrupted"] += 1

        except Exception as e:
            logger.error(f"Error processing {instance.pk}: {e}")
            continue

    logger.info(f"Integrity check completed: {results}")
    return results


# ==============================================================================
# ЗАДАЧА ОЧИСТКИ И ОПТИМИЗАЦИИ
# ==============================================================================

@shared_task
def cleanup_orphaned_files(model_label: str, field_name: str, dry_run: bool = True):
    """Очищает файлы-сироты (без соответствующих записей в БД)"""
    logger.info(f"Cleaning orphaned files for {model_label}.{field_name}, dry_run={dry_run}")

    Model = apps.get_model(model_label)

    # Получаем все пути файлов из БД
    db_paths = set()

    for instance in Model.objects.all():
        try:
            field, file, storage, name = _resolve_field(instance, field_name)

            if name:
                db_paths.add(name)

                # Добавляем связанные файлы
                hls_field = getattr(field, "hls_playlist_field", None)
                if hls_field:
                    hls_path = getattr(instance, hls_field, None)
                    if hls_path:
                        db_paths.add(hls_path)

                dash_field = getattr(field, "dash_manifest_field", None)
                if dash_field:
                    dash_path = getattr(instance, dash_field, None)
                    if dash_path:
                        db_paths.add(dash_path)

                # Добавляем превью
                preview_field = getattr(field, "preview_field", None)
                if preview_field:
                    preview_path = getattr(instance, preview_field, None)
                    if preview_path:
                        db_paths.add(preview_path)

        except Exception as e:
            logger.warning(f"Error processing {instance.pk}: {e}")
            continue

    # Здесь нужна логика сканирования storage и поиска сирот
    # Это зависит от конкретного storage backend

    orphaned_files = []  # Заглушка

    if not dry_run:
        deleted = 0
        for file_path in orphaned_files:
            try:
                # storage.delete(file_path)  # Раскомментировать для реального удаления
                deleted += 1
            except Exception as e:
                logger.error(f"Failed to delete {file_path}: {e}")

        logger.info(f"Deleted {deleted} orphaned files")
        return {"deleted": deleted, "found": len(orphaned_files)}
    else:
        logger.info(f"Found {len(orphaned_files)} orphaned files (dry run)")
        return {"found": len(orphaned_files), "files": orphaned_files[:50]}  # Первые 50


# ==============================================================================
# ПЕРИОДИЧЕСКИЕ ЗАДАЧИ (для Celery Beat)
# ==============================================================================

# В settings.py добавить:
# CELERY_BEAT_SCHEDULE = {
#     'cleanup-temp-files': {
#         'task': 'hlsfield.tasks.cleanup_old_temp_files',
#         'schedule': crontab(hour=2, minute=0),  # Каждый день в 2:00
#     },
#     'update-video-stats': {
#         'task': 'hlsfield.tasks.update_video_statistics',
#         'schedule': crontab(minute='*/30'),  # Каждые 30 минут
#     },
#     'monitor-performance': {
#         'task': 'hlsfield.tasks.monitor_transcoding_performance',
#         'schedule': crontab(minute='*/10'),  # Каждые 10 минут
#     },
# }


# ==============================================================================
# ЭКСПОРТ
# ==============================================================================

__all__ = [
    # Основные задачи
    'build_hls_for_field',
    'build_hls_for_field_sync',
    'build_dash_for_field',
    'build_dash_for_field_sync',
    'build_adaptive_for_field',
    'build_adaptive_for_field_sync',

    # Прогрессивные задачи
    'build_progressive_for_field',
    'build_progressive_for_field_sync',

    # Batch операции
    'batch_optimize_videos',
    'optimize_existing_video',
    'health_check_videos',

    # Обслуживание
    'cleanup_old_temp_files',
    'regenerate_missing_previews',
    'update_video_statistics',
    'migrate_old_video_format',
    'validate_video_integrity',
    'cleanup_orphaned_files',

    # Аналитика
    'generate_video_analytics_report',
    'monitor_transcoding_performance',
]
