"""YouTube Data API v3 episode upload.

Requires ``google-api-python-client`` and ``google-auth-oauthlib`` to be
installed, and a valid OAuth2 credential file at
``settings.youtube_client_secrets_file``.

The upload is intentionally rate-limited: YouTube allows ~6 uploads/day on
standard quota.  This module enforces that cap via a per-day upload counter
stored in a sidecar JSON file.

If the youtube libraries are not installed or credentials are missing,
:func:`upload_episode` returns a failure result without crashing.

Usage
-----
    from content.publishing.youtube import upload_episode

    result = await upload_episode(
        video_path="/tmp/episodes/ep001.mp4",
        title="Top Highlights — March 2026",
        description="Today's best moments from the stream.",
        tags=["highlights", "gaming"],
        thumbnail_path="/tmp/thumb.jpg",
    )
"""

from __future__ import annotations

import asyncio
import json
import logging
from dataclasses import dataclass
from datetime import date
from pathlib import Path

from config import settings

logger = logging.getLogger(__name__)

_UPLOADS_PER_DAY_MAX = 6


@dataclass
class YouTubeUploadResult:
    """Result of a YouTube upload attempt."""

    success: bool
    video_id: str | None = None
    video_url: str | None = None
    error: str | None = None


def _youtube_available() -> bool:
    """Return True if the google-api-python-client library is importable."""
    try:
        import importlib.util

        return (
            importlib.util.find_spec("googleapiclient") is not None
            and importlib.util.find_spec("google_auth_oauthlib") is not None
        )
    except Exception:
        return False


def _daily_upload_count() -> int:
    """Return the number of YouTube uploads performed today."""
    counter_path = Path(settings.content_youtube_counter_file)
    today = str(date.today())
    if not counter_path.exists():
        return 0
    try:
        data = json.loads(counter_path.read_text())
        return data.get(today, 0)
    except Exception:
        return 0


def _increment_daily_upload_count() -> None:
    """Increment today's upload counter."""
    counter_path = Path(settings.content_youtube_counter_file)
    counter_path.parent.mkdir(parents=True, exist_ok=True)
    today = str(date.today())
    try:
        data = json.loads(counter_path.read_text()) if counter_path.exists() else {}
    except Exception:
        data = {}
    data[today] = data.get(today, 0) + 1
    counter_path.write_text(json.dumps(data))


def _build_youtube_client():
    """Build an authenticated YouTube API client from stored credentials."""
    from google.oauth2.credentials import Credentials  # type: ignore[import]
    from googleapiclient.discovery import build  # type: ignore[import]

    creds_file = settings.content_youtube_credentials_file
    if not creds_file or not Path(creds_file).exists():
        raise FileNotFoundError(
            f"YouTube credentials not found: {creds_file!r}. "
            "Set CONTENT_YOUTUBE_CREDENTIALS_FILE to the path of your "
            "OAuth2 token JSON file."
        )
    creds = Credentials.from_authorized_user_file(creds_file)
    return build("youtube", "v3", credentials=creds)


def _upload_sync(
    video_path: str,
    title: str,
    description: str,
    tags: list[str],
    category_id: str,
    privacy_status: str,
    thumbnail_path: str | None,
) -> YouTubeUploadResult:
    """Synchronous YouTube upload — run in a thread."""
    try:
        from googleapiclient.http import MediaFileUpload  # type: ignore[import]
    except ImportError as exc:
        return YouTubeUploadResult(success=False, error=f"google libraries missing: {exc}")

    try:
        youtube = _build_youtube_client()
    except Exception as exc:
        return YouTubeUploadResult(success=False, error=str(exc))

    body = {
        "snippet": {
            "title": title,
            "description": description,
            "tags": tags,
            "categoryId": category_id,
        },
        "status": {"privacyStatus": privacy_status},
    }

    media = MediaFileUpload(video_path, chunksize=-1, resumable=True)
    try:
        request = youtube.videos().insert(
            part=",".join(body.keys()),
            body=body,
            media_body=media,
        )
        response = None
        while response is None:
            _, response = request.next_chunk()
    except Exception as exc:
        return YouTubeUploadResult(success=False, error=f"upload failed: {exc}")

    video_id = response.get("id", "")
    video_url = f"https://www.youtube.com/watch?v={video_id}" if video_id else None

    # Set thumbnail if provided
    if thumbnail_path and Path(thumbnail_path).exists() and video_id:
        try:
            youtube.thumbnails().set(
                videoId=video_id,
                media_body=MediaFileUpload(thumbnail_path),
            ).execute()
        except Exception as exc:
            logger.warning("Thumbnail upload failed (non-fatal): %s", exc)

    _increment_daily_upload_count()
    return YouTubeUploadResult(success=True, video_id=video_id, video_url=video_url)


async def upload_episode(
    video_path: str,
    title: str,
    description: str = "",
    tags: list[str] | None = None,
    thumbnail_path: str | None = None,
    category_id: str = "20",  # Gaming
    privacy_status: str = "public",
) -> YouTubeUploadResult:
    """Upload an episode video to YouTube.

    Enforces the 6-uploads-per-day quota.  Wraps the synchronous upload in
    ``asyncio.to_thread`` to avoid blocking the event loop.

    Parameters
    ----------
    video_path:
        Local path to the MP4 file.
    title:
        Video title (max 100 chars for YouTube).
    description:
        Video description.
    tags:
        List of tag strings.
    thumbnail_path:
        Optional path to a JPG/PNG thumbnail image.
    category_id:
        YouTube category ID (default "20" = Gaming).
    privacy_status:
        "public", "unlisted", or "private".

    Returns
    -------
    YouTubeUploadResult
        Always returns a result; never raises.
    """
    if not _youtube_available():
        logger.warning("google-api-python-client not installed — YouTube upload disabled")
        return YouTubeUploadResult(
            success=False,
            error="google libraries not available — pip install google-api-python-client google-auth-oauthlib",
        )

    if not Path(video_path).exists():
        return YouTubeUploadResult(success=False, error=f"video file not found: {video_path!r}")

    if _daily_upload_count() >= _UPLOADS_PER_DAY_MAX:
        return YouTubeUploadResult(
            success=False,
            error=f"daily upload quota reached ({_UPLOADS_PER_DAY_MAX}/day)",
        )

    try:
        return await asyncio.to_thread(
            _upload_sync,
            video_path,
            title[:100],
            description,
            tags or [],
            category_id,
            privacy_status,
            thumbnail_path,
        )
    except Exception as exc:
        logger.warning("YouTube upload error: %s", exc)
        return YouTubeUploadResult(success=False, error=str(exc))
