"""Haber seçici — RSS akışından tweet'lenmeye değer olanları ayıklar.

Sorun: kaynaklar günde yüzlerce haber döküyor; çoğu basketbol, voleybol,
magazin ya da tıklama tuzağı. Burada puanlama yapılır:
  + futbol/takım/lig anahtar kelimeleri
  + transfer, sakatlık, teknik direktör gibi değerli konular
  - alakasız spor dalları, magazin, reklam
Eşik üstü haberler kuyruğa girer, diğerleri 'skipped' işaretlenir.
"""
import logging
import re
from datetime import datetime, timedelta

from sqlalchemy.orm import Session

from app.db import models, crud
from app.news_engine import rss

log = logging.getLogger("news.curator")

# --- puan artıranlar -------------------------------------------------------
TEAMS = [
    "galatasaray", "fenerbahçe", "fenerbahce", "beşiktaş", "besiktas",
    "trabzonspor", "başakşehir", "basaksehir", "adana demirspor", "konyaspor",
    "sivasspor", "kayserispor", "antalyaspor", "alanyaspor", "rizespor",
    "samsunspor", "gaziantep", "hatayspor", "kasımpaşa", "kasimpasa",
    "göztepe", "goztepe", "eyüpspor", "eyupspor", "bodrum", "pendikspor",
    "boluspor", "bandırmaspor", "sakaryaspor", "erzurumspor", "manisa",
    "real madrid", "barcelona", "manchester", "liverpool", "arsenal",
    "chelsea", "bayern", "juventus", "milan", "inter", "psg", "atletico",
    "tottenham", "napoli", "roma", "dortmund", "ajax", "porto", "benfica",
    "sevilla", "valencia", "villarreal", "betis", "lazio", "atalanta",
    "fiorentina", "leipzig", "leverkusen", "monaco", "lyon", "marsilya",
    "marseille", "lens", "lille", "newcastle", "aston villa", "everton",
    "west ham", "brighton", "wolves", "southampton", "watford", "leeds",
    "sporting", "feyenoord", "psv", "celtic", "rangers", "olympiakos",
]
LEAGUE_WORDS = [
    "süper lig", "super lig", "trendyol", "1. lig", "şampiyonlar ligi",
    "sampiyonlar ligi", "avrupa ligi", "konferans ligi", "premier lig",
    "la liga", "serie a", "bundesliga", "ligue 1", "uefa", "fifa", "tff",
    "milli takım", "milli takim", "a milli",
]
TOPIC_STRONG = [
    "transfer", "imza", "anlaşma", "anlasma", "bonservis", "sözleşme",
    "sozlesme", "ayrılık", "ayrilik", "teknik direktör", "teknik direktor",
    "sakatlık", "sakatlik", "kadro", "ilk 11", "derbi", "şampiyon",
    "sampiyon", "kupa", "ceza", "tahkim", "pfdk", "hakem", "var",
]
TOPIC_WEAK = ["maç", "mac", "gol", "puan", "lig", "futbol", "forma", "stat"]

# genel futbol sözlüğü — yabancı kulüp/oyuncu adlarını bilmesek de futbolu tanı
FOOTBALL_WORDS = [
    "futbol", "gol", "penaltı", "penalti", "kırmızı kart", "sarı kart",
    "maç", "mac", "lig", "puan", "forma", "stat", "kaleci", "forvet",
    "orta saha", "stoper", "teknik direktör", "teknik direktor", "kadro",
    "devre", "hakem", "taraftar", "kupa", "galibiyet", "mağlubiyet",
    "maglubiyet", "beraberlik", "şampiyon", "sampiyon", "sezon", "fikstür",
]

# --- puan düşürenler -------------------------------------------------------
NEGATIVE = [
    "basketbol", "voleybol", "tenis", "formula", "boks", "güreş", "gures",
    "hentbol", "yüzme", "yuzme", "atletizm", "motogp", "nba", "euroleague",
    "at yarışı", "at yarisi", "e-spor", "espor", "olimpiyat", "buz hokeyi",
    "reklam", "çekiliş", "cekilis", "burç", "burc", "magazin", "instagram",
    "loto", "iddaa kupon", "bilet satış", "artistik yüzme", "para yüzücü",
    "tırmanış", "tirmanis", "motosiklet", "okçuluk", "okculuk", "judo",
    "tekvando", "karate", "halter", "eskrim", "bisiklet turu", "ufc",
]

# SEO/doldurma içerik — haber değeri yok, ajans hesabı bunları paylaşmaz
FILLER_PATTERNS = [
    "saat kaçta", "saat kacta", "hangi kanalda", "ne zaman", "canlı izle",
    "canli izle", "şifresiz", "sifresiz", "nasıl izlenir", "nasil izlenir",
    "sonuçları açıklandı", "sonuclari aciklandi", "sorgulama", "sorgu ekranı",
    "hangi takımda", "kimdir", "kaç yaşında", "kac yasinda", "nereli",
    "muhtemel 11", "İşte o anlar", "iste o anlar", "sosyal medya yıkıldı",
    "günün maçları", "gunun maclari", "nereden izlenir", "maç programı",
    "bugün kimlerin", "bugunku maclar", "hangi kanaldan", "ne zaman, saat",
    "biletleri satışa", "bilet satisa", "veda töreni canlı", "canlı yayın linki",
]

TRANSFER_WORDS = ["transfer", "imza", "bonservis", "sözleşme", "sozlesme",
                  "anlaşma", "anlasma", "kiralık", "kiralik"]

# Anahtar kelime puanı FİNAL KARAR DEĞİL, ucuz bir ön elemedir: bariz çöpü
# (diğer spor dalları, SEO doldurma) eler ve kalanları önceliklendirir.
# "Arsenal 3-0 Man City" gibi haberlerde futbol sözlüğü hiç geçmeyebiliyor,
# bu yüzden eşik düşük tutulur; asıl kaliteyi DeepSeek kapısı belirler
# (bkz. news_engine/writer.py).
MIN_SCORE = 2

# Yalnızca son yarım saatin haberleri paylaşılır: bir spor ajansı 6 saat
# önceki gelişmeyi "haber" diye atmaz. Tarama 20 dk'da bir döndüğü için
# 30 dk pencere hiçbir haberi kaçırmadan tazeliği garanti eder.
MAX_AGE_MINUTES = 30

# Kaydetme penceresi biraz daha geniş: pubDate'i eksik/hatalı olan kaynaklar
# ve panelde geçmişi görebilmek için.
KEEP_AGE_HOURS = 6
# 0.72 fazla yüksekti: "Fenerbahçe - Lyon maçına Alman hakem" ile
# "F.Bahçe - Lyon maçına Alman hakem" 0.67'de kalıp ikisi de geçiyordu.
TITLE_SIM_THRESHOLD = 0.55


def _norm(text: str) -> str:
    return (text or "").lower()


def score_item(title: str, summary: str) -> tuple[int, str]:
    """(puan, kategori) döner. Yüksek puan = daha çok tweet'lenmeye değer."""
    tl = _norm(title)
    blob = tl + " " + _norm(summary)
    score = 0

    # Başka spor dalı / alakasız içerik: sert ele
    if any(word in blob for word in NEGATIVE):
        score -= 8

    # SEO doldurma başlıkları ("maç saat kaçta", "kimdir"...): ajans bunu paylaşmaz
    if any(pat in tl for pat in FILLER_PATTERNS):
        score -= 7

    # Bilinen takım adı geçiyorsa güçlü sinyal
    if any(team in blob for team in TEAMS):
        score += 4
    # Lig/turnuva adı
    if any(word in blob for word in LEAGUE_WORDS):
        score += 3
    # Değerli konu (transfer, sakatlık, hoca, ceza...)
    if any(word in blob for word in TOPIC_STRONG):
        score += 3
    # Genel futbol sözlüğü — yabancı kulüp/oyuncu tanınmasa da futbolu yakala
    if any(word in blob for word in FOOTBALL_WORDS):
        score += 2

    # Salt röportaj alıntısı olan başlıklar ("...") düşük haber değeri taşır
    if tl.startswith(("'", '"', "“", "‘")) or tl.count('"') >= 2:
        score -= 2

    if len(title) < 25:
        score -= 1

    category = "transfer" if any(w in blob for w in TRANSFER_WORDS) else "news"
    return score, category


# Basında yaygın kısaltmalar — benzerlik karşılaştırması öncesi tekleştirilir
ABBREV = {
    "f.bahçe": "fenerbahçe", "fbahçe": "fenerbahçe", "f bahçe": "fenerbahçe",
    "g.saray": "galatasaray", "gsaray": "galatasaray", "g saray": "galatasaray",
    "b.jk": "beşiktaş", "bjk": "beşiktaş",
    "t.spor": "trabzonspor", "tspor": "trabzonspor",
}


def _title_tokens(title: str) -> set:
    """Başlık benzerliği için sadeleştirilmiş kelime kümesi."""
    t = _norm(title)
    for short, full in ABBREV.items():
        t = t.replace(short, full)
    t = re.sub(r"[^\wçğıöşü\s]", " ", t)
    return {w for w in t.split() if len(w) > 3}


def _too_similar(tokens: set, others: list[set]) -> bool:
    """Jaccard benzerliği — aynı haberin farklı kaynaktaki kopyasını ele."""
    if not tokens:
        return False
    for other in others:
        if not other:
            continue
        inter = len(tokens & other)
        union = len(tokens | other)
        if union and inter / union >= TITLE_SIM_THRESHOLD:
            return True
    return False


def ingest(db: Session) -> dict:
    """RSS'i tarar, yeni haberleri puanlayıp DB'ye yazar."""
    stats = {"fetched": 0, "new": 0, "relevant": 0}
    items = rss.fetch_all()
    stats["fetched"] = len(items)
    if not items:
        return stats

    existing = {
        h for (h,) in db.query(models.NewsItem.url_hash)
        .filter(models.NewsItem.url_hash.in_([i["url_hash"] for i in items]))
        .all()
    }
    cutoff = datetime.utcnow() - timedelta(hours=KEEP_AGE_HOURS)

    # Son 24 saatte kaydedilmiş alakalı başlıkların token kümeleri:
    # aynı haberin farklı kaynaktaki kopyasını elemek için.
    recent_titles = [
        _title_tokens(t) for (t,) in
        db.query(models.NewsItem.title)
        .filter(models.NewsItem.created_at >= datetime.utcnow() - timedelta(hours=24),
                models.NewsItem.skipped == False)  # noqa: E712
        .all()
    ]

    for it in items:
        if it["url_hash"] in existing:
            continue
        pub = it["published_at"]
        if pub and pub < cutoff:
            continue  # bayat haberi hiç kaydetme
        score, category = score_item(it["title"], it["summary"])
        relevant = score >= MIN_SCORE

        if relevant:
            tokens = _title_tokens(it["title"])
            if _too_similar(tokens, recent_titles):
                relevant = False       # aynı haberin kopyası
            else:
                recent_titles.append(tokens)

        db.add(models.NewsItem(
            source=it["source"], title=it["title"], summary=it["summary"],
            url=it["url"], url_hash=it["url_hash"], category=category,
            score=score, published_at=pub, skipped=not relevant,
        ))
        stats["new"] += 1
        if relevant:
            stats["relevant"] += 1

    db.commit()
    if stats["new"]:
        crud.add_log(
            db, f"Haber taraması: {stats['new']} yeni, {stats['relevant']} alakalı",
            source="news",
        )
    return stats


def pick_for_tweet(db: Session, limit: int = 1) -> list[models.NewsItem]:
    """Tweet'lenecek haberleri seçer: alakalı, SON 30 DAKİKA, henüz atılmamış.

    Yayın zamanı bilinmeyen haberler için sisteme giriş zamanı esas alınır
    (kaynak pubDate vermezse haber bayat mı taze mi bilemeyiz; ilk kez şimdi
    gördüysek taze kabul etmek makul).
    """
    cutoff = datetime.utcnow() - timedelta(minutes=MAX_AGE_MINUTES)
    q = (
        db.query(models.NewsItem)
        .filter(
            models.NewsItem.tweeted == False,   # noqa: E712
            models.NewsItem.skipped == False,   # noqa: E712
        )
        .filter(
            ((models.NewsItem.published_at != None) &   # noqa: E711
             (models.NewsItem.published_at >= cutoff))
            |
            ((models.NewsItem.published_at == None) &   # noqa: E711
             (models.NewsItem.created_at >= cutoff))
        )
        .order_by(models.NewsItem.score.desc(), models.NewsItem.id.desc())
    )
    return q.limit(limit).all()
