"""
pipeline.py
-----------
End-to-end orchestrator: transcribe → summarize → SEO.

Each stage is a pure function over Pydantic schemas. The orchestrator (run_pipeline)
chains them. When running over RabbitMQ, each stage function becomes the body of
a queue consumer — see rabbit_integration.py.

Direct (in-process) usage:
    from .pipeline import run_pipeline
    from .schemas import TranscriptionInput, MediaMeta, SummarizationTask

    result = run_pipeline(
        inp=TranscriptionInput(audio_path="/data/audio.wav", video_id="vid_001"),
        meta=MediaMeta(domain="https://typicallyoutliers.com", video_path="/data/vid.mp4"),
        task=SummarizationTask.ABSTRACT,
    )
    print(result.seo.seo_title)
    print(result.seo.schema_markup)
"""
from __future__ import annotations

import logging
import os
from typing import Optional

from .model_registry import ModelKey, get_model
from .schemas import (
    MediaMeta,
    PipelineResult,
    SummarizationRequest,
    SummarizationTask,
    TranscriptionInput,
    TranscriptionResult,
    WhisperSegment,
)
from .seo_builder import build_seo_payload
from .summarizer import summarize

logger = logging.getLogger(__name__)


# ── Stage 1: Transcription ────────────────────────────────────────────────────

def stage_transcribe(inp: TranscriptionInput) -> TranscriptionResult:
    """
    Transcription stage. Pure function — given a TranscriptionInput, returns
    a TranscriptionResult. No side effects.

    When text is provided directly, it is passed through without calling Whisper.
    """
    if inp.text:
        logger.info("[Stage:Transcribe] Passthrough — text provided directly.")
        return TranscriptionResult(
            video_id=inp.video_id or "passthrough",
            text=inp.text,
            segments=[],
            language=inp.language,
            source="passthrough",
        )

    audio_path = _resolve_audio_path(inp)
    logger.info("[Stage:Transcribe] Running Whisper on %s", audio_path)

    whisper_model = get_model(ModelKey.WHISPER)
    raw = whisper_model.transcribe(audio_path, language=inp.language)

    segments = [
        WhisperSegment(start=s["start"], end=s["end"], text=s["text"])
        for s in (raw.get("segments") or [])
    ]
    video_id = inp.video_id or _infer_video_id(audio_path)
    text     = raw.get("text") or ""

    logger.info("[Stage:Transcribe] Done. %d chars, %d segments.", len(text), len(segments))
    return TranscriptionResult(
        video_id=video_id,
        text=text,
        segments=segments,
        language=inp.language,
        source="whisper",
    )


def _resolve_audio_path(inp: TranscriptionInput) -> str:
    """
    Resolve a ready audio file path from the input.
    Handles: audio_path directly, extraction from video_path, download from video_url.
    """
    if inp.audio_path and os.path.isfile(inp.audio_path):
        return inp.audio_path

    if inp.video_path and os.path.isfile(inp.video_path):
        from .functions import extract_audio_from_video
        audio_dir  = os.path.dirname(inp.video_path)
        audio_path = os.path.join(audio_dir, "audio.wav")
        result     = extract_audio_from_video(
            video_path=inp.video_path,
            audio_path=audio_path,
        )
        if result and os.path.isfile(result):
            return result
        raise RuntimeError(
            f"[Stage:Transcribe] Audio extraction failed for video: {inp.video_path}"
        )

    if inp.video_url:
        from .functions import get_whisper_audio_path
        audio_path = get_whisper_audio_path(video_url=inp.video_url)
        if audio_path and os.path.isfile(audio_path):
            return audio_path
        raise RuntimeError(
            f"[Stage:Transcribe] Could not obtain audio from URL: {inp.video_url}"
        )

    raise ValueError(
        "[Stage:Transcribe] No valid audio source. "
        "Provide audio_path, video_path, video_url, or text."
    )


def _infer_video_id(audio_path: str) -> str:
    return os.path.splitext(os.path.basename(audio_path))[0]


# ── Stage 2: Summarization ────────────────────────────────────────────────────

def stage_summarize(
    transcription: TranscriptionResult,
    task:          SummarizationTask = SummarizationTask.ABSTRACT,
    keyword_count: int = 10,
    min_length:    int = 100,
    max_length:    int = 512,
) -> "SummarizationResult":  # noqa: F821
    """
    Summarization stage. Pure function over schemas.
    """
    from .schemas import SummarizationResult  # local import to avoid circular at module level
    logger.info(
        "[Stage:Summarize] video_id=%s task=%s keywords=%d",
        transcription.video_id, task, keyword_count,
    )
    req    = SummarizationRequest(
        transcription=transcription,
        task=task,
        min_length=min_length,
        max_length=max_length,
        keyword_count=keyword_count,
    )
    result = summarize(req)
    logger.info(
        "[Stage:Summarize] Done. keywords=%s refined_len=%d",
        result.keywords[:3], len(result.refined_summary),
    )
    return result


# ── Stage 3: SEO ──────────────────────────────────────────────────────────────

def stage_seo(
    summary_result:   "SummarizationResult",
    transcription:    TranscriptionResult,
    meta:             MediaMeta,
    title:            Optional[str] = None,
) -> "SEOPayload":  # noqa: F821
    """
    SEO stage. Pure function over schemas.
    """
    logger.info("[Stage:SEO] video_id=%s", summary_result.video_id)
    whisper_segments = [s.model_dump() for s in transcription.segments]
    payload = build_seo_payload(
        summary_result=summary_result,
        meta=meta,
        title=title,
        whisper_segments=whisper_segments,
    )
    logger.info("[Stage:SEO] Done. title='%s'", payload.seo_title)
    return payload


# ── Orchestrator ──────────────────────────────────────────────────────────────

def run_pipeline(
    inp:           TranscriptionInput,
    meta:          MediaMeta,
    task:          SummarizationTask = SummarizationTask.ABSTRACT,
    keyword_count: int = 10,
    title:         Optional[str] = None,
) -> PipelineResult:
    """
    Run all three stages in sequence (in-process).
    For distributed execution, replace this with the RabbitMQ publisher in
    rabbit_integration.py and run each stage_* function inside a consumer.
    """
    logger.info("[Pipeline] Start. video_id=%s", inp.video_id or "?")

    transcription  = stage_transcribe(inp)
    summarization  = stage_summarize(transcription, task=task, keyword_count=keyword_count)
    seo            = stage_seo(summarization, transcription, meta, title=title)

    logger.info("[Pipeline] Complete. video_id=%s", transcription.video_id)
    return PipelineResult(
        video_id=transcription.video_id,
        transcription=transcription,
        summarization=summarization,
        seo=seo,
    )
