#!/usr/bin/env python3
"""Collect football source messages into an isolated fan feed. No publishing."""

import asyncio
import hashlib
import sqlite3
from pathlib import Path

from telethon import TelegramClient
import news_collector as shared

BASE = Path(__file__).resolve().parent
SOURCE_SESSION = Path(str(shared.SESSION_PATH) + ".session")
FAN_SESSION = BASE / "sessions" / "fan_reader.session"

MAX_MESSAGES_PER_SOURCE = 20


def ensure_session_copy():
    if FAN_SESSION.exists():
        return
    if not SOURCE_SESSION.is_file():
        raise RuntimeError("Authorized news_reader.session was not found.")
    FAN_SESSION.parent.mkdir(parents=True, exist_ok=True)
    source = sqlite3.connect(
        "file:" + SOURCE_SESSION.as_posix() + "?mode=ro",
        uri=True
    )
    destination = sqlite3.connect(str(FAN_SESSION))
    try:
        source.backup(destination)
    finally:
        destination.close()
        source.close()
    FAN_SESSION.chmod(0o600)


def load_sources():
    db = shared.get_db()
    try:
        with db.cursor() as cursor:
            cursor.execute("""
                SELECT s.id, s.name, s.username,
                       COALESCE(st.last_message_id, 0) AS last_message_id
                FROM sources s
                JOIN fan_news_source_settings fs
                  ON fs.source_id = s.id AND fs.is_enabled = 1
                LEFT JOIN fan_news_collection_state st
                       ON st.source_id = s.id
                WHERE 1 = 1
                  AND s.is_active = 1
                  AND s.source_kind = 'telegram'
                ORDER BY s.id
            """)
            return cursor.fetchall()
    finally:
        db.close()


def record_error(source_id, message):
    db = shared.get_db()
    try:
        with db.cursor() as cursor:
            cursor.execute("""
                INSERT INTO fan_news_collection_state (source_id, last_error)
                VALUES (%s, %s)
                ON DUPLICATE KEY UPDATE last_error = VALUES(last_error)
            """, (source_id, str(message)[:1000]))
    finally:
        db.close()


def save_source_messages(source, messages):
    db = shared.get_db()
    inserted = 0
    latest = int(source["last_message_id"] or 0)

    try:
        with db.cursor() as cursor:
            for message in sorted(messages, key=lambda item: item.id):
                message_id = int(message.id)
                text = str(message.message or "").strip()

                # Keep the cursor moving past service events and media-only posts.
                if text:
                    published_at = (
                        message.date.replace(tzinfo=None)
                        if message.date else None
                    )
                    cursor.execute("""
                        INSERT IGNORE INTO fan_news_feed_items (
                            source_id, source_message_id,
                            telegram_grouped_id, published_at,
                            text_content, has_media, source_views,
                            source_forwards, content_hash, feed_status
                        ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, 'raw')
                    """, (
                        source["id"],
                        message_id,
                        getattr(message, "grouped_id", None),
                        published_at,
                        text,
                        int(message.media is not None),
                        getattr(message, "views", None),
                        getattr(message, "forwards", None),
                        hashlib.sha256(text.encode("utf-8")).hexdigest(),
                    ))
                    inserted += cursor.rowcount

                latest = max(latest, message_id)

            # Advance state only after all inserts for this source succeeded.
            cursor.execute("""
                INSERT INTO fan_news_collection_state (
                    source_id, last_message_id, last_collected_at, last_error
                ) VALUES (%s, %s, NOW(), NULL)
                ON DUPLICATE KEY UPDATE
                    last_message_id = GREATEST(last_message_id, VALUES(last_message_id)),
                    last_collected_at = NOW(),
                    last_error = NULL
            """, (source["id"], latest))
    finally:
        db.close()

    return inserted, latest


async def main():
    publisher = shared.get_publisher()
    if not publisher or int(publisher.get("is_active") or 0) != 1:
        raise RuntimeError("Publisher #1 is missing or inactive.")

    api_id = int(str(publisher.get("telegram_api_id") or "").strip())
    api_hash = str(publisher.get("telegram_api_hash") or "").strip()
    if not api_id or not api_hash:
        raise RuntimeError("Telegram API credentials are missing.")

    ensure_session_copy()
    client = TelegramClient(
        str(FAN_SESSION.with_suffix("")),
        api_id,
        api_hash
    )
    await client.connect()

    try:
        if not await client.is_user_authorized():
            raise RuntimeError("Fan reader session is not authorized.")

        sources = load_sources()
        print(f"FOOTBALL SOURCES: {len(sources)}", flush=True)

        for source in sources:
            username = str(source.get("username") or "").strip().lstrip("@")
            if not username:
                record_error(source["id"], "Missing username")
                print(f"source={source['id']} missing username", flush=True)
                continue

            try:
                entity = await client.get_entity("@" + username)
                messages = [
                    message
                    async for message in client.iter_messages(
                        entity,
                        min_id=int(source["last_message_id"] or 0),
                        limit=MAX_MESSAGES_PER_SOURCE,
                        # Existing sources: consume unread messages in order.
                        # New sources: bootstrap with recent messages only.
                        reverse=int(source["last_message_id"] or 0) > 0
                    )
                ]
                inserted, latest = save_source_messages(source, messages)
                print(
                    f"source={source['id']} @{username} "
                    f"read={len(messages)} inserted={inserted} "
                    f"last_id={latest}",
                    flush=True
                )
            except Exception as exc:
                record_error(source["id"], f"{type(exc).__name__}: {exc}")
                print(
                    f"source={source['id']} ERROR "
                    f"{type(exc).__name__}: {exc}",
                    flush=True
                )
                if type(exc).__name__ == "FloodWaitError":
                    break
    finally:
        await client.disconnect()


if __name__ == "__main__":
    asyncio.run(main())
