"""
Übungs-Fließtext: Inline-Verweise auf exercise_media.id (MEDIA_ASSETS_AND_ARCHIVE_SPEC §11).

- Kurzsyntax beim Speichern → kanonisches <span data-shinkan-exercise-media="…">.
- Verweise müssen für die betreffende Übung gültige exercise_media-Zeilen sein.
"""

from __future__ import annotations

import re
from typing import FrozenSet, Optional, Set

from fastapi import HTTPException

# {{exerciseMedia:123}} (Whitespace tolerant, case-insensitive Schlüssel)
_BRACE_PATTERN = re.compile(r"\{\{\s*exerciseMedia\s*:\s*(\d+)\s*\}\}", re.IGNORECASE)
# bereits gespeichertes Markup (einfache Anführungszeichen-varianten durch Regex abgedeckt)
_DATA_ATTR_PATTERN = re.compile(r'data-shinkan-exercise-media\s*=\s*["\']?(\d+)["\']?', re.IGNORECASE)

RICH_HTML_EXERCISE_FIELDS: FrozenSet[str] = frozenset(
    {"summary", "goal", "execution", "preparation", "trainer_notes"}
)


def normalize_inline_exercise_media_markup(html: Optional[str]) -> Optional[str]:
    """Wandelt {{exerciseMedia:id}} in kanonisches span mit data-shinkan-exercise-media."""

    if html is None:
        return None
    if not isinstance(html, str):
        html = str(html)
    stripped = html.strip()
    if not stripped:
        return html

    def _repl(match: re.Match) -> str:
        mid = int(match.group(1))
        return (
            f'<span data-shinkan-exercise-media="{mid}" data-shinkan-exercise-media-size="medium" '
            f'class="shinkan-inline-media"></span>'
        )

    return _BRACE_PATTERN.sub(_repl, html)


def collect_inline_exercise_media_ids(html: Optional[str]) -> Set[int]:
    """Sammelt alle referenzierten exercise_media.ids aus Kurzsyntax und kanonischem Span."""
    if html is None or not isinstance(html, str):
        return set()
    if not html.strip():
        return set()
    ids: Set[int] = set()
    ids.update(int(m) for m in _BRACE_PATTERN.findall(html))
    ids.update(int(m) for m in _DATA_ATTR_PATTERN.findall(html))
    return ids


def assert_no_inline_media_references_on_create(ids: Set[int]) -> None:
    """Neue Übung hat noch keine exercise_media-Zeilen — Platzhalter verboten."""

    if not ids:
        return
    raise HTTPException(
        status_code=400,
        detail={
            "code": "INLINE_EXERCISE_MEDIA_ON_CREATE",
            "message": (
                "Medienverweise im Fließtext sind beim ersten Anlegen der Übung nicht möglich. "
                "Bitte Übung ohne Platzhalter speichern, Medien hochladen oder verknüpfen "
                "und die Verweise dann bearbeiten ({{exerciseMedia:id}} oder „Medium einfügen“)."
            ),
            "invalid_exercise_media_ids": sorted(ids),
        },
    )


def validate_inline_exercise_media_ids_for_exercise(cur, exercise_id: int, ids: Set[int]) -> None:
    """Prüft, dass jede genannte exercise_media.id zu dieser Übung gehört."""

    if not ids:
        return
    sid = sorted(ids)
    ph = ",".join(["%s"] * len(sid))
    cur.execute(
        f"SELECT id FROM exercise_media WHERE exercise_id = %s AND id IN ({ph})",
        (exercise_id, *sid),
    )
    found = set()
    for row in cur.fetchall():
        rid = row["id"] if isinstance(row, dict) else row[0]
        found.add(int(rid))
    missing = ids - found
    if missing:
        raise HTTPException(
            status_code=400,
            detail={
                "code": "INLINE_EXERCISE_MEDIA_INVALID",
                "message": "Ein oder mehrere eingebettete Medien-Verweise gehören nicht zu dieser Übung.",
                "invalid_exercise_media_ids": sorted(missing),
            },
        )
