"""Kaskaden-Dispatcher fuer Podcast-Transkript-Bezug.

Reihenfolge der Strategien:
  1. rss_native     — Podcasting-2.0-Tag <podcast:transcript> im Feed-Entry
  2. website_*      — Redaktionelles Manuskript auf der Episoden-Webseite
                      (sender-spezifische Adapter)

Episoden ohne Treffer in einer der Stufen werden verworfen (kein Fehler).
YouTube-Fallback wird nicht genutzt.

Jeder Adapter implementiert:
    def can_handle(feed_entry: dict, feed_url: str) -> bool
    async def fetch(feed_entry: dict, feed_url: str) -> TranscriptResult | None

Wer None liefert, gibt der naechsten Stufe die Chance. Wer einen
TranscriptResult liefert, beendet die Kaskade fuer diese Episode.

Der Dispatcher kuemmert sich um das Caching gegen die Tabelle
`podcast_transcripts` — eine einmal gefundene Episode wird bei folgenden
Refreshes (auch in anderen Lagen) direkt aus dem Cache geholt.
"""
from __future__ import annotations

import json
import logging
from dataclasses import dataclass
from typing import Optional

logger = logging.getLogger("osint.podcast.extractors")


@dataclass
class TranscriptResult:
    """Einheitliches Ergebnis einer Transkript-Strategie."""
    text: str
    source: str                       # "rss_native" / "website_scrape"
    segments: Optional[list] = None   # Optional: [{"start": sec, "end": sec, "text": "..."}]


# Reihenfolge der Kaskade: zuerst Feed-Tag, dann Senderseiten
from . import rss_native
from . import website_dlf
from . import website_sz
from . import website_spiegel
from . import website_ndr

_EXTRACTORS = [
    rss_native,
    website_dlf,
    website_sz,
    website_spiegel,
    website_ndr,
]


async def fetch_transcript(feed_entry: dict, feed_url: str, episode_url: str) -> Optional[TranscriptResult]:
    """Versucht Kaskade durch bis eine Stufe liefert.

    Vor dem Kaskaden-Lauf wird der Cache (Tabelle `podcast_transcripts`) gegen
    episode_url geprueft. Trifft der Cache, wird ohne HTTP-Request ausgeliefert.
    """
    if not episode_url:
        return None

    from database import get_db
    db = await get_db()
    try:
        cursor = await db.execute(
            "SELECT transcript, source, segments_json FROM podcast_transcripts WHERE url = ?",
            (episode_url,),
        )
        row = await cursor.fetchone()
        if row:
            segments = None
            if row["segments_json"]:
                try:
                    segments = json.loads(row["segments_json"])
                except json.JSONDecodeError:
                    segments = None
            logger.debug(f"Transkript-Cache-Hit: {episode_url}")
            return TranscriptResult(text=row["transcript"], source=row["source"], segments=segments)
    finally:
        await db.close()

    # Kaskade: erste Stufe, die can_handle(True) und ein Ergebnis liefert, gewinnt.
    for extractor in _EXTRACTORS:
        try:
            if not extractor.can_handle(feed_entry, feed_url):
                continue
            result = await extractor.fetch(feed_entry, feed_url)
            if result and result.text and result.text.strip():
                await _store_in_cache(episode_url, result)
                logger.info(
                    f"Transkript via {result.source} fuer {episode_url} "
                    f"({len(result.text)} Zeichen)"
                )
                return result
        except Exception as e:
            logger.warning(f"Extraktor {extractor.__name__} fuer {episode_url}: {e}")
            continue

    logger.debug(f"Kein Transkript verfuegbar: {episode_url}")
    return None


async def _store_in_cache(url: str, result: TranscriptResult) -> None:
    """Legt das Transkript in der Cache-Tabelle ab (INSERT OR REPLACE)."""
    from database import get_db
    db = await get_db()
    try:
        segments_json = json.dumps(result.segments, ensure_ascii=False) if result.segments else None
        await db.execute(
            "INSERT OR REPLACE INTO podcast_transcripts (url, transcript, source, segments_json) "
            "VALUES (?, ?, ?, ?)",
            (url, result.text, result.source, segments_json),
        )
        await db.commit()
    except Exception as e:
        logger.warning(f"Cache-Write fuer {url} fehlgeschlagen: {e}")
    finally:
        await db.close()
