#!/usr/bin/env python3

import asyncio
import re
import sys
from pathlib import Path

import pymysql

from telethon import TelegramClient, functions, types
from telethon.errors import (
    UserAlreadyParticipantError,
    ChannelPrivateError,
    UsernameInvalidError,
    UsernameNotOccupiedError,
    FloodWaitError,
)


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

PUBLISHER_ID = 1


def fail(message: str, code: int = 1):
    print("")
    print("❌ ERROR")
    print(message)
    sys.exit(code)


def read_php_database_config():
    if not DB_CONFIG.exists():
        fail(f"Database config not found: {DB_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 '{key}' "
                "from config/database.php"
            )

        result[key] = match.group(1)

    return result


def get_database():
    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 = %s
              AND is_active = 1
            LIMIT 1
            """,
            (PUBLISHER_ID,)
        )

        return cursor.fetchone()


def build_client(publisher):
    api_id_raw = str(
        publisher.get("telegram_api_id") or ""
    ).strip()

    api_hash = str(
        publisher.get("telegram_api_hash") or ""
    ).strip()

    session_name = str(
        publisher.get("session_name") or ""
    ).strip()

    if not api_id_raw:
        fail("Telegram API ID missing.")

    try:
        api_id = int(api_id_raw)
    except ValueError:
        fail("Telegram API ID must be numeric.")

    if not api_hash:
        fail("Telegram API Hash missing.")

    if not session_name:
        session_name = f"publisher_{PUBLISHER_ID}"

    session_name = re.sub(
        r"[^a-zA-Z0-9_-]",
        "_",
        session_name
    )

    session_path = SESSION_DIR / session_name

    return TelegramClient(
        str(session_path),
        api_id,
        api_hash
    )


def normalize_username(value):
    value = str(value or "").strip()

    value = re.sub(
        r"^https?://(?:www\.)?t\.me/",
        "",
        value,
        flags=re.I
    )

    value = value.strip().lstrip("@").strip("/")

    return value


def find_existing_source(db, username, telegram_chat_id):
    with db.cursor() as cursor:

        if telegram_chat_id:
            cursor.execute(
                """
                SELECT *
                FROM sources
                WHERE telegram_chat_id = %s
                LIMIT 1
                """,
                (telegram_chat_id,)
            )

            row = cursor.fetchone()

            if row:
                return row

        cursor.execute(
            """
            SELECT *
            FROM sources
            WHERE LOWER(username) = LOWER(%s)
            LIMIT 1
            """,
            (username,)
        )

        return cursor.fetchone()


def create_or_update_source(
    db,
    username,
    telegram_chat_id,
    title
):
    existing = find_existing_source(
        db,
        username,
        telegram_chat_id
    )

    with db.cursor() as cursor:

        if existing:

            cursor.execute(
                """
                UPDATE sources
                SET
                    source_kind = 'telegram',
                    provider = 'Telegram',
                    name = %s,
                    telegram_chat_id = %s,
                    username = %s,
                    source_type = 'telegram',
                    is_active = 1,
                    health_status = 'healthy',
                    last_success_at = NOW(),
                    last_error = NULL,
                    last_error_at = NULL
                WHERE id = %s
                LIMIT 1
                """,
                (
                    title,
                    telegram_chat_id,
                    username,
                    existing["id"]
                )
            )

            return int(existing["id"]), False

        cursor.execute(
            """
            INSERT INTO sources
            (
                channel_id,
                source_kind,
                provider,
                name,
                telegram_chat_id,
                username,
                source_language,
                target_language,
                translate_enabled,
                rewrite_enabled,
                source_type,
                weight,
                poll_interval_seconds,
                is_active,
                health_status,
                last_success_at
            )
            VALUES
            (
                NULL,
                'telegram',
                'Telegram',
                %s,
                %s,
                %s,
                'fa',
                'fa',
                0,
                0,
                'telegram',
                1.00,
                60,
                1,
                'healthy',
                NOW()
            )
            """,
            (
                title,
                telegram_chat_id,
                username
            )
        )

        return int(cursor.lastrowid), True


def attach_sport_source(
    db,
    source_id,
    sport_type="football",
    source_role="seed"
):
    with db.cursor() as cursor:

        cursor.execute(
            """
            INSERT INTO sport_sources
            (
                source_id,
                source_role,
                sport_type,
                priority,
                is_active,
                last_error,
                consecutive_errors
            )
            VALUES
            (
                %s,
                %s,
                %s,
                1.00,
                1,
                NULL,
                0
            )
            ON DUPLICATE KEY UPDATE
                source_role = VALUES(source_role),
                priority = VALUES(priority),
                is_active = 1,
                last_error = NULL,
                consecutive_errors = 0
            """,
            (
                source_id,
                source_role,
                sport_type
            )
        )


async def process(username, execute=False):

    username = normalize_username(username)

    if not username:
        fail("Username is empty.")

    db = get_database()

    try:
        publisher = get_publisher(db)

        if not publisher:
            fail(
                f"Active publisher #{PUBLISHER_ID} not found."
            )

        client = build_client(publisher)

        await client.connect()

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

            print("")
            print("SPORT SOURCE SEED WORKER V1")
            print("==============================================")
            print(f"Mode:           {'EXECUTE' if execute else 'PREVIEW'}")
            print(f"Username:       @{username}")
            print("Sport:          football")
            print("Role:           seed")
            print("==============================================")

            entity = await client.get_entity(username)

            telegram_chat_id = str(
                getattr(entity, "id", "") or ""
            )

            title = str(
                getattr(entity, "title", "") or ""
            ).strip()

            real_username = str(
                getattr(entity, "username", "") or username
            ).strip()

            print("")
            print(f"Resolved Name:  {title}")
            print(f"Resolved ID:    {telegram_chat_id}")
            print(f"Resolved User:  @{real_username}")

            existing = find_existing_source(
                db,
                real_username,
                telegram_chat_id
            )

            if existing:
                print(f"Master Source:  EXISTS #{existing['id']}")
            else:
                print("Master Source:  NEW")

            if not execute:
                print("")
                print("PREVIEW ONLY")
                print("No Telegram join performed.")
                print("No database changes performed.")
                return

            try:
                await client(
                    functions.channels.JoinChannelRequest(
                        channel=entity
                    )
                )

                print("Telegram:       JOINED")

            except UserAlreadyParticipantError:
                print("Telegram:       ALREADY JOINED")

            source_id, created = create_or_update_source(
                db,
                real_username,
                telegram_chat_id,
                title
            )

            print(
                f"Master Source:  #{source_id} "
                f"({'CREATED' if created else 'UPDATED'})"
            )

            attach_sport_source(
                db,
                source_id,
                "football",
                "seed"
            )

            print("Sport Source:   ATTACHED")

            try:
                await client(
                    functions.account.UpdateNotifySettingsRequest(
                        peer=entity,
                        settings=types.InputPeerNotifySettings(
                            mute_until=2147483647
                        )
                    )
                )

                print("Mute:           FOREVER")

            except Exception as exc:
                print(f"Mute:           FAILED - {exc}")

            try:
                await client.edit_folder(
                    entity,
                    folder=1
                )

                print("Archive:        DONE")

            except Exception as exc:
                print(f"Archive:        FAILED - {exc}")

            print("")
            print("SPORT SEED INSTALL COMPLETE")

        finally:
            await client.disconnect()

    finally:
        db.close()


def usage():
    print("")
    print("SPORT SOURCE SEED WORKER V1")
    print("")
    print("Usage:")
    print(
        "  python3 engine/sport_source_seed_worker.py "
        "preview @username"
    )
    print(
        "  python3 engine/sport_source_seed_worker.py "
        "execute @username"
    )
    print("")


def main():

    if len(sys.argv) < 3:
        usage()
        return

    mode = sys.argv[1].strip().lower()
    username = sys.argv[2]

    if mode == "preview":
        execute = False

    elif mode == "execute":
        execute = True

    else:
        usage()
        fail("Mode must be preview or execute.")

    try:
        asyncio.run(
            process(
                username,
                execute
            )
        )

    except FloodWaitError as exc:
        fail(
            f"Telegram FloodWait: "
            f"{exc.seconds} seconds"
        )

    except (
        ChannelPrivateError,
        UsernameInvalidError,
        UsernameNotOccupiedError
    ) as exc:
        fail(
            f"Telegram channel unavailable: {exc}"
        )

    except KeyboardInterrupt:
        print("")
        print("Stopped.")

    except Exception as exc:
        fail(
            f"{type(exc).__name__}: {exc}"
        )


if __name__ == "__main__":
    main()
