"""
URL-Extraktion und Normalisierung für den NotebookLM-Kurator.

Funktionen:
    extract_urls(text) -> list[dict]   — findet alle URLs in beliebigem Text
    normalize(url)     -> tuple[str,str] — gibt (kind, normalized_url) zurück
"""

import re
from urllib.parse import urlparse, parse_qs, urlencode, urlunparse


# Tracking-Parameter, die bei Web-URLs entfernt werden
_WEB_TRACKING_PARAMS = {
    "utm_source", "utm_medium", "utm_campaign", "utm_term", "utm_content",
    "fbclid", "gclid", "mc_cid", "mc_eid", "ref", "ref_src",
    "_hsenc", "_hsmi",
}

# Tracking-Parameter, die bei YouTube-URLs entfernt werden (alles außer "v")
_YT_REMOVE_PARAMS = {
    "si", "t", "feature", "pp", "list", "index", "start_radio", "ab_channel",
}

# Regex für YouTube-Video-IDs: base64url-Alphabet
_YT_ID_RE = re.compile(r"[A-Za-z0-9_-]+")

# Grober URL-Finder für beliebigen Text
# Erfasst http/https-URLs, stoppt an Whitespace oder typischen Markdown-Trennzeichen
_URL_FINDER = re.compile(
    r"https?://[^\s\)\]\>\"'<,;]+"
)


def _extract_youtube_id(parsed) -> str | None:
    """
    Extrahiert die YouTube-Video-ID aus einem bereits geparsten URL-Objekt.
    Gibt None zurück, wenn keine gültige ID gefunden wird.
    """
    host = parsed.netloc.lower()
    path = parsed.path

    # youtu.be/<id>
    if host == "youtu.be":
        m = _YT_ID_RE.match(path.lstrip("/"))
        return m.group(0) if m else None

    # youtube.com/watch?v=<id>
    if "/watch" in path:
        qs = parse_qs(parsed.query)
        ids = qs.get("v", [])
        return ids[0] if ids else None

    # youtube.com/shorts/<id>  oder  youtube.com/embed/<id>
    for prefix in ("/shorts/", "/embed/"):
        if path.startswith(prefix):
            segment = path[len(prefix):].split("/")[0].split("?")[0]
            m = _YT_ID_RE.fullmatch(segment)
            return segment if m else None

    return None


def _is_youtube(host: str) -> bool:
    return host in {
        "youtube.com", "www.youtube.com", "m.youtube.com",
        "youtu.be", "www.youtu.be",
    }


def normalize(url: str) -> tuple[str, str]:
    """
    Normalisiert eine URL und klassifiziert sie.

    Returns:
        (kind, normalized_url)
        kind ist "youtube" oder "web"
    """
    parsed = urlparse(url)
    host = parsed.netloc.lower()

    if _is_youtube(host):
        video_id = _extract_youtube_id(parsed)
        if video_id:
            canonical = f"https://www.youtube.com/watch?v={video_id}"
            return ("youtube", canonical)
        # Kein Video-ID erkennbar → als Web-URL behandeln
        return ("web", _normalize_web(url))

    return ("web", _normalize_web(url))


def _normalize_web(url: str) -> str:
    """
    Normalisiert eine Web-URL:
    - Schema und Host lowercase
    - Tracking-Parameter entfernen
    - Verbleibende Query-Parameter alphabetisch sortieren
    - Fragment behalten, außer er beginnt mit utm_
    """
    parsed = urlparse(url)
    scheme = parsed.scheme.lower()
    host = parsed.netloc.lower()
    path = parsed.path  # Pfad unverändert (inkl. trailing slash)

    # Query bereinigen
    qs = parse_qs(parsed.query, keep_blank_values=True)
    cleaned = {
        k: v for k, v in qs.items()
        if k.lower() not in _WEB_TRACKING_PARAMS
        and not k.lower().startswith("utm_")
    }
    # Alphabetisch sortiert, Mehrfachwerte flach (erster Wert)
    sorted_query = urlencode(
        sorted((k, v[0]) for k, v in cleaned.items())
    )

    # Fragment: behalten, außer Tracking-Fragment
    fragment = parsed.fragment
    if fragment.lower().startswith("utm_"):
        fragment = ""

    normalized = urlunparse((scheme, host, path, "", sorted_query, fragment))
    return normalized


def extract_urls(text: str) -> list[dict]:
    """
    Findet alle HTTP/HTTPS-URLs in beliebigem Text (Markdown, Plaintext).

    Returns:
        Liste von Dicts: {"original": str, "normalized": str, "kind": str}
        Dedupliziert nach normalized-URL.
    """
    raw_urls = _URL_FINDER.findall(text)

    seen: set[str] = set()
    result: list[dict] = []

    for raw in raw_urls:
        # Markdown-Artefakte am Ende entfernen (z.B. trailing ")." oder "]")
        raw = raw.rstrip(").]>,;")
        try:
            kind, normalized = normalize(raw)
        except Exception:
            continue

        if normalized in seen:
            continue
        seen.add(normalized)
        result.append({"original": raw, "normalized": normalized, "kind": kind})

    return result
