# ---------------------- Inline serializers for documentation only ----------------------
# Using inline_serializer to avoid creating new files.

import yt_dlp
import tempfile
import os
import shutil

from rest_framework import serializers
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated, AllowAny
from drf_spectacular.utils import extend_schema, inline_serializer
from drf_spectacular.types import OpenApiTypes
from django.conf import settings
from django.http import StreamingHttpResponse
from django.utils.text import slugify
# NEW: aggregations and timeseries helpers
from django.db import models
from django.utils import timezone
from django.db.models.functions import TruncDay, TruncHour
from .models import DownloaderRecord

# Allowed container formats for output/remux
FORMAT_CHOICES = ("mp4", "mkv", "webm", "flv", "mov", "avi", "ogg")
FORMAT_HELP = (
    "Choose container format: "
    "mp4 (H.264 + AAC, most compatible), "
    "mkv (flexible, lossless container), "
    "webm (VP9/AV1 + Opus), "
    "flv (legacy), mov (Apple-friendly), "
    "avi (older), ogg (mostly obsolete)."
)

# Minimal mime map by extension
MIME_BY_EXT = {
    "mp4": "video/mp4",
    "mkv": "video/x-matroska",
    "webm": "video/webm",
    "flv": "video/x-flv",
    "mov": "video/quicktime",
    "avi": "video/x-msvideo",
    "ogg": "video/ogg",
}

class Downloader(APIView):
    permission_classes = [AllowAny]
    authentication_classes = []

    @extend_schema(
        tags=["downloader"],
        summary="Get video info from URL",
        parameters=[
            inline_serializer(
                name="VideoInfoParams",
                fields={
                    "url": serializers.URLField(help_text="Video URL to analyze"),
                },
            )
        ],
        responses={
            200: inline_serializer(
                name="VideoInfoResponse",
                fields={
                    "title": serializers.CharField(),
                    "duration": serializers.IntegerField(allow_null=True),
                    "thumbnail": serializers.URLField(allow_null=True),
                    "video_resolutions": serializers.ListField(child=serializers.CharField()),
                    "audio_resolutions": serializers.ListField(child=serializers.CharField()),
                },
            ),
            400: inline_serializer(
                name="ErrorResponse",
                fields={"error": serializers.CharField()},
            ),
        },
    )
    def get(self, request):
        url = request.data.get("url") or request.query_params.get("url")
        if not url:
            return Response({"error": "URL is required"}, status=400)

        ydl_options = {
            "quiet": True,
        }
        try:
            with yt_dlp.YoutubeDL(ydl_options) as ydl:
                info = ydl.extract_info(url, download=False)
        except Exception:
            return Response({"error": "Failed to retrieve video info"}, status=400)

        formats = info.get("formats", []) or []

        # Video: collect unique heights and sort desc
        heights = {
            int(f.get("height"))
            for f in formats
            if f.get("vcodec") != "none" and isinstance(f.get("height"), int)
        }
        video_resolutions = [f"{h}p" for h in sorted(heights, reverse=True)]

        # Audio: collect unique bitrates (abr kbps), fallback to tbr when abr missing
        bitrates = set()
        for f in formats:
            if f.get("acodec") != "none" and f.get("vcodec") == "none":
                abr = f.get("abr")
                tbr = f.get("tbr")
                val = None
                if isinstance(abr, (int, float)):
                    val = int(abr)
                elif isinstance(tbr, (int, float)):
                    val = int(tbr)
                if val and val > 0:
                    bitrates.add(val)

        audio_resolutions = [f"{b}kbps" for b in sorted(bitrates, reverse=True)]

        return Response(
            {
                "title": info.get("title"),
                "duration": info.get("duration"),
                "thumbnail": info.get("thumbnail"),
                "video_resolutions": video_resolutions,
                "audio_resolutions": audio_resolutions,
            },
            status=200,
        )
    


    @extend_schema(
        tags=["downloader"],
        summary="Download video from URL",
        request=inline_serializer(
            name="DownloadRequest",
            fields={
                "url": serializers.URLField(help_text="Video URL to download"),
                "ext": serializers.ChoiceField(
                    choices=FORMAT_CHOICES,
                    required=False,
                    default="mp4",
                    help_text=FORMAT_HELP,
                ),
                "format": serializers.ChoiceField(
                    choices=FORMAT_CHOICES, 
                    required=False, 
                    help_text="Alias of 'ext' (deprecated)."
                ),
                "video_quality": serializers.IntegerField(
                    required=True, 
                    help_text="Target max video height (e.g. 1080)."
                ),
                "audio_quality": serializers.IntegerField(
                    required=True, 
                    help_text="Target max audio bitrate in kbps (e.g. 160)."
                ),
            },
        ),
        responses={
            200: OpenApiTypes.BINARY,
            400: inline_serializer(
                name="DownloadErrorResponse",
                fields={
                    "error": serializers.CharField(),
                    "allowed": serializers.ListField(child=serializers.CharField(), required=False),
                },
            ),
        },
    )
    def post(self, request):
        url = request.data.get("url")
        # Accept ext or legacy format param
        ext = (request.data.get("ext") or request.data.get("format") or "mp4").lower()
        
        try:
            video_quality = int(request.data.get("video_quality"))  # height, e.g., 1080
            audio_quality = int(request.data.get("audio_quality"))  # abr kbps, e.g., 160
        except Exception:
            return Response({"error": "Invalid quality parameters, not integers!"}, status=400)
        
        if not url:
            return Response({"error": "URL is required"}, status=400)
        if ext not in FORMAT_CHOICES:
            return Response({"error": f"Unsupported extension '{ext}'", "allowed": FORMAT_CHOICES}, status=400)

        # Ensure base tmp dir exists
        os.makedirs(settings.DOWNLOADER_TMP_DIR, exist_ok=True)
        tmpdir = tempfile.mkdtemp(prefix="downloader_", dir=settings.DOWNLOADER_TMP_DIR)
        outtmpl = os.path.join(tmpdir, "download.%(ext)s")

        # Build a format selector using requested quality caps
        # Example: "bv[height<=1080]+ba[abr<=160]/b"
        video_part = f"bv[height<={video_quality}]" if video_quality else "bv*"
        audio_part = f"ba[abr<={audio_quality}]" if audio_quality else "ba"

        format_selector = f"{video_part}+{audio_part}/b"


        ydl_options = {
            "format": format_selector,            # select by requested quality
            "merge_output_format": ext,           # container
            "outtmpl": outtmpl,                   # temp dir
            "quiet": True,
            "max_filesize": settings.DOWNLOADER_MAX_SIZE_BYTES,
            "socket_timeout": settings.DOWNLOADER_TIMEOUT,
            # remux to container without re-encoding where possible
            "postprocessors": [
                {"key": "FFmpegVideoRemuxer", "preferedformat": ext}
            ],
        }

        file_path = ""
        try:
            with yt_dlp.YoutubeDL(ydl_options) as ydl:
                info = ydl.extract_info(url, download=True)
                base = ydl.prepare_filename(info)
                file_path = base if base.endswith(f".{ext}") else os.path.splitext(base)[0] + f".{ext}"

            # Stats before streaming
            duration = int((info or {}).get("duration") or 0)
            size = os.path.getsize(file_path) if os.path.exists(file_path) else 0

            DownloaderRecord.objects.create(
                url=url,
                format=ext,
                length_of_media=duration,
                file_size=size,
            )

            # Streaming generator that deletes file & temp dir after send (or on abort)
            def stream_and_cleanup(path: str, temp_dir: str, chunk_size: int = 8192):
                try:
                    with open(path, "rb") as f:
                        while True:
                            chunk = f.read(chunk_size)
                            if not chunk:
                                break
                            yield chunk
                finally:
                    try:
                        if os.path.exists(path):
                            os.remove(path)
                    finally:
                        shutil.rmtree(temp_dir, ignore_errors=True)

            safe_title = slugify(info.get("title") or "video")
            filename = f"{safe_title}.{ext}"
            content_type = MIME_BY_EXT.get(ext, "application/octet-stream")

            response = StreamingHttpResponse(
                streaming_content=stream_and_cleanup(file_path, tmpdir),
                content_type=content_type,
            )
            if size:
                response["Content-Length"] = str(size)
            response["Content-Disposition"] = f'attachment; filename="{filename}"'

            return response

        except Exception as e:
            shutil.rmtree(tmpdir, ignore_errors=True)
            return Response({"error": str(e)}, status=400)
        




# ----------------  STATS FOR GRAPHS  ----------------

from .serializers import DownloaderStatsSerializer
from django.db.models import Count, Avg, Sum

class DownloaderStats(APIView):
    """
    Vrací agregované statistiky z tabulky DownloaderRecord.
    """
    authentication_classes = []
    permission_classes = [AllowAny]
    @extend_schema(
        tags=["downloader"],
        summary="Get aggregated downloader statistics",
        responses={200: DownloaderStatsSerializer},
    )
    def get(self, request):
        # agregace číselných polí
        agg = DownloaderRecord.objects.aggregate(
            total_downloads=Count("id"),
            avg_length_of_media=Avg("length_of_media"),
            avg_file_size=Avg("file_size"),
            total_length_of_media=Sum("length_of_media"),
            total_file_size=Sum("file_size"),
        )

        # zjištění nejčastějšího formátu
        most_common = (
            DownloaderRecord.objects.values("format")
            .annotate(count=Count("id"))
            .order_by("-count")
            .first()
        )

        agg["most_common_format"] = most_common["format"] if most_common else None

        serializer = DownloaderStatsSerializer(agg)
        return Response(serializer.data)