"""Haber -> tweet yazıcı.

İki iş birden yapar:
  1. KALİTE KAPISI: haber gerçekten futbolla ilgili ve paylaşmaya değer mi?
     Anahtar kelime listesi futbol bilgisini kodlayamaz ("Arsenal 3-0 Man City"
     cümlesinde tek bir futbol kelimesi yok), bu yüzden kararı LLM verir.
  2. YAZIM: uygunsa doğal, ajans üslubunda Türkçe tweet üretir.

LLM yoksa/erişilemezse: sadece yüksek puanlı haberler şablonla paylaşılır
(düşük puanlılar atlanır — hatalı paylaşmaktansa atlamak yeğdir).
"""
import logging

from sqlalchemy.orm import Session

from app.db import models, crud
from app.content_engine import templates, generator, textutil
from app.news_engine import article
import config

log = logging.getLogger("news.writer")

MAX_LEN = 280
SKIP_TOKEN = "ATLA"

# LLM yokken şablonla paylaşmak için gereken minimum güven puanı
TEMPLATE_FALLBACK_MIN_SCORE = 9

SYSTEM_PROMPT = (
    "Sen aktif bir Türk futbol haber ajansısın. Sana bir haberin başlığı ve TAM "
    "METNİ verilir. Görevin, haberi okuyup tek bir tweet halinde özetlemek.\n\n"
    "EN ÖNEMLİ KURAL: Tweet KENDİ BAŞINA ANLAŞILIR olmalı. Okuyan kişi haberin "
    "linkine tıklamadan, başka hiçbir şey bilmeden ne olduğunu anlamalı.\n"
    "- Kim, ne yaptı/ne oldu, sonuç ne — bunlar tweet'te AÇIKÇA yazsın.\n"
    "- Başlığı olduğu gibi kopyalama. Başlıklar çoğu zaman havada kalır "
    "('Italiano'dan Vlahovic sözleri!' hiçbir şey anlatmaz). Metinden asıl "
    "bilgiyi çıkar ve onu yaz: kim ne dedi, ne karar verildi, skor kaç.\n"
    "- Gerekli bağlamı ekle: takım adı, lig, skor, rakam, tarih.\n"
    "- Merak uyandırıp bilgiyi saklama ('işte o detaylar', 'belli oldu' deyip "
    "bırakma). Bilgiyi doğrudan ver.\n"
    "- Haberde olmayan hiçbir bilgi UYDURMA.\n\n"
    "BİÇİM: En fazla 250 karakter. Doğal, akıcı Türkçe. 1-2 uygun emoji. "
    "Abartı ve klişe yok.\n"
    "SATIR DÜZENİ: Tweet'i okunaklı kur. Ana bilgiyi ilk satıra yaz; ek "
    "ayrıntı varsa ALT SATIRA geçir (satır sonu kullan). Duvar gibi tek blok "
    "metin yazma, ama gereksiz yere de bölme — en fazla 2-3 satır.\n"
    "Sadece tweet metnini döndür; tırnak, başlık, açıklama, hashtag ekleme.\n\n"
    f"YALNIZCA şu durumlarda tweet yerine SADECE '{SKIP_TOKEN}' yaz:\n"
    "- Haber futbolla ilgili değilse (basketbol, voleybol, yüzme, motor sporları)\n"
    "- 'Maç saat kaçta / hangi kanalda / nereden izlenir' türü yayın bilgisiyse\n"
    "- Loto/çekiliş/kupon, reklam, magazin veya astroloji içeriğiyse\n"
    "- Metin o kadar boşsa ki anlaşılır bir tweet yazmak mümkün değilse\n\n"
    "Bunların dışındaki her futbol haberini tweet'e çevir."
)


def _build_prompt(item: models.NewsItem) -> str:
    parts = [f"Başlık: {item.title}"]
    if item.full_text:
        parts.append(f"\nHaberin tam metni:\n{item.full_text}")
    elif item.summary:
        parts.append(f"\nÖzet: {item.summary[:600]}")
    parts.append(f"\nKaynak: {item.source}")
    return "\n".join(parts)


def _llm_write(db: Session, item: models.NewsItem, api_key: str) -> str | None:
    """Tweet metni, ya da atlanacaksa None."""
    try:
        client = generator._get_client(api_key)
        # Reasoning payı için bol bütçe (yalnız üretilen kadar ödenir);
        # 800'de yarım kalan metinler iyi haberleri boşa elemişti.
        resp = client.chat.completions.create(
            model=config.DEEPSEEK_MODEL,
            messages=[
                {"role": "system", "content": SYSTEM_PROMPT},  # sabit -> cache
                {"role": "user", "content": _build_prompt(item)},
            ],
            temperature=0.9,
            max_tokens=10000,
        )
        choice = resp.choices[0]
        if choice.finish_reason == "length":
            # 10k'da bile yarım: bozuk tweet atmaktansa atla
            crud.add_log(db, "Haber tweeti sınıra takıldı (10k!), atlandı",
                         level="warn", source="news")
            return None
        text = (choice.message.content or "").strip().strip('"').strip()
        if not text:
            return None
        if SKIP_TOKEN in text.upper()[:20]:
            return None      # LLM "paylaşma" dedi
        return text
    except Exception as e:
        crud.add_log(db, f"Haber yazımı (DeepSeek) hatası: {e}",
                     level="warn", source="news")
        return "__LLM_ERROR__"


def write_tweet(db: Session, item: models.NewsItem) -> str | None:
    """Haberden tweet üretir. Paylaşılmamalıysa None döner."""
    engine = crud.get_setting(db, "content_engine", "deepseek")
    api_key = crud.get_setting(db, "deepseek_api_key", "")

    # Tam metni burada çek: her taranan habere değil, yalnızca gerçekten
    # tweet'lenecek olana istek atılır (yüzlerce gereksiz indirme olmasın).
    if not item.full_text:
        item.full_text = article.fetch_full_text(item.url)
        db.commit()

    if engine == "deepseek" and api_key:
        text = _llm_write(db, item, api_key)
        if text is None:
            return None                     # LLM eledi
        if text != "__LLM_ERROR__":
            return _finish(text, db)
        # LLM hatası -> aşağıdaki şablon yoluna düş

    # Şablon yolu: LLM yok. Yalnız yüksek güvenli haberleri paylaş.
    if item.score < TEMPLATE_FALLBACK_MIN_SCORE:
        return None
    kind = "transfer" if item.category == "transfer" else "news"
    text = templates.render(kind, {"detail": item.title})
    return _finish(text, db) if text else None


def _finish(text: str, db: Session = None) -> str:
    """Satır yapısını KORUYARAK düzenler ve 280'e sığdırır.

    Eskiden `" ".join(text.split())` yapılıyordu; bu, LLM'in koyduğu satır
    sonlarını da eziyor ve tweetler tek blok hâlinde okunmaz çıkıyordu.
    Ayrıca kaynak marka etiketleri (#beINSPORTS) temizlenir.
    """
    if db is not None:
        handles = [h for (h,) in db.query(models.SourceAccount.handle).all()]
        text = textutil.strip_hashtags(
            text, textutil.block_tags_from_sources(handles))
    return textutil.fit(textutil.normalize(text), MAX_LEN)
