"""Kanonische Medien-Identitaet je Domain.

Hintergrund: Quellennamen kommen aus drei Richtungen in den Bestand, aus dem
Feed-Namen (RSS), aus dem Hostnamen (staan-Treffer im EU-Weg) und aus der freien
Benennung durch das Modell in der Abschlussauswahl. Dadurch fuehrt derselbe
Verlag mehrere Identitaeten, etwa "Guardian UK", "Guardian World" und
"The Guardian" oder "tagesschau" und "tagesschau.de". Jede Aussage ueber
Quellenvielfalt oder Belegtiefe wird damit falsch, weil dieselbe Redaktion
mehrfach gezaehlt wird.

Dieses Modul bestimmt die Identitaet ueber die Domain, nicht ueber den Namen.
Der Anzeigename wird datengetrieben gewaehlt: der haeufigste Name, der fuer
diese Domain vorkommt. Die Tabelle unten deckt nur die Faelle ab, in denen gar
kein brauchbarer Name vorliegt oder der Hostname als Name durchschlaegt.
"""
from collections import Counter
from urllib.parse import urlparse

# Zusammengesetzte Endungen, bei denen die registrierbare Domain drei Labels
# hat (bbc.co.uk statt co.uk).
_MEHRTEILIGE_ENDUNGEN = frozenset({
    "co.uk", "org.uk", "ac.uk", "gov.uk", "me.uk", "net.uk",
    "com.au", "net.au", "org.au", "gov.au",
    "co.jp", "or.jp", "ne.jp", "go.jp",
    "com.br", "com.mx", "com.ar", "com.tr", "com.cn", "com.hk",
    "co.za", "co.nz", "co.kr", "co.in", "co.il",
    "or.at", "co.at", "gv.at",
})

# Praefixe, die dieselbe Redaktion nur anders ausliefern.
_HOST_PRAEFIXE = ("www.", "m.", "amp.", "rss.", "feeds.", "news.", "english.", "de.", "en.")

# Anzeigename, wenn kein brauchbarer Name mitgeliefert wurde. Bewusst kurz
# gehalten, die Liste ist keine Pflegepflicht: unbekannte Domains bekommen
# ihren ersten Domain-Bestandteil in Grossschreibung.
_NAMEN_JE_DOMAIN = {
    "tagesschau.de": "tagesschau",
    "zdfheute.de": "ZDF heute",
    "zdf.de": "ZDF heute",
    "faz.net": "FAZ",
    "sueddeutsche.de": "Süddeutsche Zeitung",
    "spiegel.de": "Spiegel",
    "zeit.de": "Zeit",
    "welt.de": "Welt",
    "n-tv.de": "n-tv",
    "dw.com": "Deutsche Welle",
    "deutschlandfunk.de": "Deutschlandfunk",
    "bundesregierung.de": "Bundesregierung",
    "nzz.ch": "NZZ",
    "srf.ch": "SRF",
    "kurier.at": "Kurier",
    "diepresse.com": "Die Presse",
    "theguardian.com": "The Guardian",
    "nytimes.com": "New York Times",
    "ft.com": "Financial Times",
    "bbc.co.uk": "BBC",
    "bbc.com": "BBC",
    "aljazeera.com": "Al Jazeera",
    "france24.com": "France24",
    "reuters.com": "Reuters",
    "apnews.com": "AP News",
    "elpais.com": "El País",
    "elmundo.es": "El Mundo",
    "abc.es": "ABC",
    "infobae.com": "Infobae",
    "politico.eu": "Politico Europe",
    "euronews.com": "Euronews",
}

# Weiterleitungs- und Sammelportale. Sie sind kein eigenstaendiges Medium und
# duerfen in keiner Vielfaltsstatistik als Redaktion durchgehen.
AGGREGATOR_DOMAINS = frozenset({
    "news.google.com", "google.com", "msn.com", "headtopics.com",
    "newsbreak.com", "yahoo.com", "flipboard.com", "smartnews.com",
    "reutersconnect.com", "pressreader.com", "onvista.de", "finanznachrichten.de",
    "boersennews.de", "aol.com",
})


def registrable_domain(url: str) -> str:
    """Registrierbare Domain einer URL, klein geschrieben.

    'https://www.theguardian.com/world/...' -> 'theguardian.com'
    'https://www.bbc.co.uk/news/...'        -> 'bbc.co.uk'
    Leere oder unparsebare Eingaben ergeben einen leeren String.
    """
    host = ""
    text = (url or "").strip()
    if not text:
        return ""
    try:
        host = (urlparse(text if "//" in text else "//" + text).hostname or "").lower()
    except ValueError:
        return ""
    if not host:
        return ""
    for praefix in _HOST_PRAEFIXE:
        if host.startswith(praefix) and host.count(".") >= 2:
            host = host[len(praefix):]
    teile = host.split(".")
    if len(teile) <= 2:
        return host
    if ".".join(teile[-2:]) in _MEHRTEILIGE_ENDUNGEN:
        return ".".join(teile[-3:])
    return ".".join(teile[-2:])


def ist_aggregator(url: str) -> bool:
    """True, wenn die URL auf ein Weiterleitungs- oder Sammelportal zeigt."""
    return registrable_domain(url) in AGGREGATOR_DOMAINS


def name_aus_domain(domain: str) -> str:
    """Anzeigename allein aus der Domain, wenn kein Name vorliegt."""
    if not domain:
        return "Unbekannt"
    if domain in _NAMEN_JE_DOMAIN:
        return _NAMEN_JE_DOMAIN[domain]
    erstes = domain.split(".")[0]
    return erstes if any(c.isupper() for c in erstes) else erstes.capitalize()


def _ist_hostname_als_name(name: str, domain: str) -> bool:
    """True, wenn der Name nur der Hostname ist ('www.france24.com')."""
    schlicht = (name or "").strip().lower()
    return bool(schlicht) and (schlicht == domain or schlicht.endswith(domain))


def namen_je_domain(eintraege: list[tuple[str, str]]) -> dict[str, str]:
    """Waehlt je Domain einen Anzeigenamen aus allen gelieferten Varianten.

    eintraege ist eine Liste aus (url, name). Gewinner ist der haeufigste Name,
    bei Gleichstand der kuerzeste. Hostnamen und leere Namen zaehlen nicht mit,
    fuer sie greift die Tabelle beziehungsweise die Domain selbst.
    """
    kandidaten: dict[str, Counter] = {}
    for url, name in eintraege:
        domain = registrable_domain(url)
        if not domain:
            continue
        zaehler = kandidaten.setdefault(domain, Counter())
        sauber = (name or "").strip()
        if sauber and not _ist_hostname_als_name(sauber, domain):
            zaehler[sauber] += 1

    ergebnis: dict[str, str] = {}
    for domain, zaehler in kandidaten.items():
        # Der kuratierte Name hat Vorrang. Sonst gewinnt der haeufigste
        # gelieferte Name, und das ist in der Praxis der Feed-Name: der
        # Guardian erschien im Bericht als "Guardian World", die New York
        # Times als "NYT Top Stories". Ein Feed ist aber kein Medium.
        if domain in _NAMEN_JE_DOMAIN:
            ergebnis[domain] = _NAMEN_JE_DOMAIN[domain]
            continue
        if not zaehler:
            ergebnis[domain] = name_aus_domain(domain)
            continue
        hoechste = max(zaehler.values())
        beste = sorted((n for n, c in zaehler.items() if c == hoechste), key=lambda n: (len(n), n))
        ergebnis[domain] = beste[0]
    return ergebnis


def sprache_aus_url(url: str) -> str:
    """Sprachkennung, die sich eindeutig aus der Adresse ergibt, sonst ''.

    Deckt die Faelle ab, in denen das Modell die Sprache falsch angibt, etwa
    'english.elpais.com' als DE. Nur eindeutige Muster, keine Raterei.
    """
    text = (url or "").strip().lower()
    if not text:
        return ""
    try:
        zerlegt = urlparse(text if "//" in text else "//" + text)
    except ValueError:
        return ""
    host = zerlegt.hostname or ""
    pfad = zerlegt.path or ""
    if host.startswith("english.") or pfad.startswith("/en/") or "/english/" in pfad:
        return "EN"
    if host.startswith("de.") or pfad.startswith("/de/"):
        return "DE"
    if host.startswith("fr.") or pfad.startswith("/fr/"):
        return "FR"
    if host.startswith("es.") or pfad.startswith("/es/"):
        return "ES"
    return ""
