#!/usr/bin/env python3

import asyncio
import hashlib
import re
import shutil
import sys
import tempfile
from pathlib import Path

import pymysql
from telethon import TelegramClient
from telethon.errors import FloodWaitError


BASE_DIR = Path(__file__).resolve().parent.parent
DB_CONFIG = BASE_DIR / "config" / "database.php"
SESSION_SOURCE = BASE_DIR / "engine" / "sessions" / "publisher_1.session"

LIMIT_PER_SOURCE = 20


def fail(message):
    print("")
    print("❌ ERROR")
    print(message)
    sys.exit(1)


def read_php_database_config():

    content = DB_CONFIG.read_text(
        encoding="utf-8",
        errors="ignore"
    )

    patterns = {
        "host": r"\$dbHost\s*=\s*['\"]([^'\"]+)['\"]",
        "name": r"\$dbName\s*=\s*['\"]([^'\"]+)['\"]",
        "user": r"\$dbUser\s*=\s*['\"]([^'\"]+)['\"]",
        "password": r"\$dbPass\s*=\s*['\"]([^'\"]*)['\"]",
    }

    result = {}

    for key, pattern in patterns.items():

        match = re.search(pattern, content)

        if not match:
            fail(f"Could not read DB config: {key}")

        result[key] = match.group(1)

    return result


def get_db():

    cfg = read_php_database_config()

    return pymysql.connect(
        host=cfg["host"],
        user=cfg["user"],
        password=cfg["password"],
        database=cfg["name"],
        charset="utf8mb4",
        cursorclass=pymysql.cursors.DictCursor,
        autocommit=True
    )


def get_publisher(db):

    with db.cursor() as cursor:

        cursor.execute("""
            SELECT *
            FROM publishers
            WHERE id = 1
              AND is_active = 1
            LIMIT 1
        """)

        return cursor.fetchone()


def get_news_sources(db):

    with db.cursor() as cursor:

        cursor.execute("""
            SELECT
                ns.id AS news_source_id,
                ns.source_id,
                ns.source_role,
                ns.priority,

                s.name,
                s.username,
                s.telegram_chat_id

            FROM news_sources ns

            INNER JOIN sources s
                ON s.id = ns.source_id

            WHERE ns.is_active = 1
              AND s.is_active = 1
              AND s.source_kind = 'telegram'

            ORDER BY
                ns.priority DESC,
                ns.id ASC
        """)

        return cursor.fetchall()


def normalize_text(text):

    text = str(text or "").strip()

    text = re.sub(
        r"\s+",
        " ",
        text
    )

    return text


def make_hash(text):

    normalized = normalize_text(text).lower()

    if not normalized:
        return None

    return hashlib.sha256(
        normalized.encode("utf-8")
    ).hexdigest()


def detect_media_type(message):

    if getattr(message, "photo", None):
        return "photo"

    document = getattr(message, "document", None)

    if document:

        mime = str(
            getattr(document, "mime_type", "")
            or ""
        ).lower()

        if mime.startswith("video/"):
            return "video"

        if mime.startswith("audio/"):
            return "audio"

        return "document"

    if getattr(message, "video", None):
        return "video"

    if getattr(message, "audio", None):
        return "audio"

    if getattr(message, "voice", None):
        return "voice"

    return None


def insert_feed_item(
    db,
    source,
    message
):

    text = str(
        getattr(message, "message", "")
        or ""
    )

    media_type = detect_media_type(message)

    content_type = (
        "mixed"
        if text and media_type
        else (
            "media"
            if media_type
            else "text"
        )
    )

    grouped_id = getattr(
        message,
        "grouped_id",
        None
    )

    views = getattr(
        message,
        "views",
        None
    )

    forwards = getattr(
        message,
        "forwards",
        None
    )

    published_at = None

    if getattr(message, "date", None):

        published_at = (
            message.date
            .replace(tzinfo=None)
            .strftime("%Y-%m-%d %H:%M:%S")
        )

    content_hash = make_hash(text)

    with db.cursor() as cursor:

        cursor.execute("""
            INSERT IGNORE INTO news_feed_items
            (
                news_source_id,
                source_id,
                source_message_id,
                telegram_grouped_id,

                published_at,

                content_type,
                text_content,
                media_type,

                source_views,
                source_forwards,

                content_hash,
                feed_status
            )
            VALUES
            (
                %s,
                %s,
                %s,
                %s,

                %s,

                %s,
                %s,
                %s,

                %s,
                %s,

                %s,
                'raw'
            )
        """, (
            source["news_source_id"],
            source["source_id"],
            int(message.id),
            grouped_id,

            published_at,

            content_type,
            text,
            media_type,

            views,
            forwards,

            content_hash
        ))

        return cursor.rowcount == 1


async def main():

    if not SESSION_SOURCE.exists():
        fail(
            f"Session not found: {SESSION_SOURCE}"
        )

    db = get_db()

    client = None

    temp_dir = Path(
        tempfile.mkdtemp(
            prefix="news_session_"
        )
    )

    temp_session = (
        temp_dir / "news_reader.session"
    )

    try:

        publisher = get_publisher(db)

        if not publisher:
            fail("Publisher #1 not found 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:
            fail(
                "Publisher #1 API credentials missing."
            )

        sources = get_news_sources(db)

        print("")
        print("NEWS COLLECTOR TEST")
        print("==============================================")
        print(f"Active Sources:   {len(sources)}")
        print(f"Limit/Source:     {LIMIT_PER_SOURCE}")
        print("Mode:             READ ONLY → RAW FEED")
        print("Session:          TEMP COPY")
        print("==============================================")

        if not sources:
            print("No active News Sources.")
            return

        #
        # Snapshot the authorized session.
        # News test never opens publisher_1.session directly.
        #
        shutil.copy2(
            SESSION_SOURCE,
            temp_session
        )

        client = TelegramClient(
            str(temp_session.with_suffix("")),
            api_id,
            api_hash
        )

        await client.connect()

        if not await client.is_user_authorized():
            fail(
                "Temporary Telegram session "
                "is not authorized."
            )

        total_scanned = 0
        total_inserted = 0
        total_duplicates = 0
        total_errors = 0

        for source in sources:

            username = str(
                source.get("username")
                or ""
            ).strip()

            if not username:
                print(
                    f"⚠️ Source #{source['source_id']} "
                    "has no username."
                )
                continue

            target = "@" + username.lstrip("@")

            print("")
            print("----------------------------------------------")
            print(
                f"Source:           {target}"
            )

            try:

                entity = await client.get_entity(
                    target
                )

                scanned = 0
                inserted = 0
                duplicates = 0

                async for message in client.iter_messages(
                    entity,
                    limit=LIMIT_PER_SOURCE
                ):

                    if not message:
                        continue

                    #
                    # Ignore service messages.
                    #
                    if not getattr(
                        message,
                        "message",
                        None
                    ) and not getattr(
                        message,
                        "media",
                        None
                    ):
                        continue

                    scanned += 1

                    if insert_feed_item(
                        db,
                        source,
                        message
                    ):
                        inserted += 1
                    else:
                        duplicates += 1

                total_scanned += scanned
                total_inserted += inserted
                total_duplicates += duplicates

                print(
                    f"Scanned:          {scanned}"
                )
                print(
                    f"Inserted:         {inserted}"
                )
                print(
                    f"Duplicates:       {duplicates}"
                )

            except FloodWaitError as exc:

                seconds = int(exc.seconds)

                print("")
                print(
                    f"⚠️ FloodWait: {seconds} seconds"
                )
                print(
                    "Collector stopped safely."
                )

                break

            except Exception as exc:

                total_errors += 1

                print(
                    f"❌ Source Error: {exc}"
                )

        print("")
        print("==============================================")
        print("RESULT")
        print("----------------------------------------------")
        print(
            f"Scanned:          {total_scanned}"
        )
        print(
            f"Inserted:         {total_inserted}"
        )
        print(
            f"Duplicates:       {total_duplicates}"
        )
        print(
            f"Errors:           {total_errors}"
        )
        print("==============================================")

    finally:

        if client is not None:

            try:
                await client.disconnect()
            except Exception:
                pass

        db.close()

        shutil.rmtree(
            temp_dir,
            ignore_errors=True
        )


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