"""
Entry point of the Telegram Video Downloader Bot.

Run with:  python main.py
"""

from __future__ import annotations

import logging
import sys

from telegram import Update
from telegram.constants import ParseMode
from telegram.ext import (
    Application,
    ApplicationBuilder,
    CallbackQueryHandler,
    CommandHandler,
    ContextTypes,
    MessageHandler,
    filters,
)

import admin
import config
import downloader
import force_join
import handlers
import keyboards
from database import Database

logger = logging.getLogger("main")


def setup_logging() -> None:
    """Configure console logging using LOG_LEVEL from .env."""
    allowed = {"CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG"}
    level_name = config.LOG_LEVEL if config.LOG_LEVEL in allowed else "INFO"
    logging.basicConfig(
        format="%(asctime)s | %(levelname)-8s | %(name)s | %(message)s",
        level=getattr(logging, level_name, logging.INFO),
    )
    # Silence noisy third-party loggers (never log secrets/tokens).
    logging.getLogger("httpx").setLevel(logging.WARNING)
    logging.getLogger("yt_dlp").setLevel(logging.WARNING)


async def post_init(app: Application) -> None:
    """Runs after the Application is built and before polling starts."""
    db: Database = app.bot_data["db"]
    await db.initialize()

    config.DOWNLOAD_PATH.mkdir(parents=True, exist_ok=True)
    config.DATABASE_PATH.parent.mkdir(parents=True, exist_ok=True)

    # Seed the force-join setting from .env on first run only.
    if await db.get_setting(force_join.FORCE_SETTINGS_KEY) is None:
        await db.set_setting(
            force_join.FORCE_SETTINGS_KEY, "1" if config.FORCE_JOIN_ENABLED else "0"
        )

    me = await app.bot.get_me()
    logger.info("Bot started: @%s (id=%s)", me.username, me.id)
    logger.info("FFmpeg available: %s", downloader.ffmpeg_available())
    logger.info("Force join enabled: %s", await force_join.is_enabled(db))
    logger.info(
        "Limits: max_file_size=%sMB max_concurrent_downloads=%s",
        config.MAX_FILE_SIZE_MB,
        config.MAX_CONCURRENT_DOWNLOADS,
    )


async def post_shutdown(app: Application) -> None:
    """Runs after polling has stopped."""
    db: Database = app.bot_data["db"]
    await db.close()
    logger.info("Bot stopped, database closed.")


async def error_handler(update: object, context: ContextTypes.DEFAULT_TYPE) -> None:
    """Global error handler: log everything, never crash the bot."""
    logger.error("Exception while handling an update:", exc_info=context.error)
    if isinstance(update, Update) and update.effective_chat is not None:
        try:
            await context.bot.send_message(
                update.effective_chat.id,
                "⚠️ خطای غیرمنتظره‌ای رخ داد. لطفاً دوباره تلاش کنید.",
                parse_mode=ParseMode.HTML,
            )
        except Exception:  # noqa: BLE001 - nothing more we can do here
            pass


def register_handlers(app: Application) -> None:
    """Register all handlers. Order matters:
    1) commands, 2) callback queries, 3) admin pending input, 4) URL handler.
    Handlers 3 and 4 share group 0; only the first match processes an update.
    """
    # Commands
    app.add_handler(CommandHandler("start", handlers.start_command))
    app.add_handler(CommandHandler("help", handlers.help_command))
    app.add_handler(CommandHandler("cancel", handlers.cancel_command))
    app.add_handler(CommandHandler("admin", admin.admin_command))
    app.add_handler(CommandHandler("stats", admin.stats_command))

    # Callback queries
    app.add_handler(CallbackQueryHandler(handlers.handle_callback, pattern=keyboards.PATTERN_USER_CB))
    app.add_handler(
        CallbackQueryHandler(handlers.handle_force_join_check, pattern=keyboards.PATTERN_FORCE_CHECK)
    )
    app.add_handler(CallbackQueryHandler(admin.handle_admin_callback, pattern=keyboards.PATTERN_ADMIN_CB))
    app.add_handler(
        CallbackQueryHandler(force_join.handle_admin_callback, pattern=keyboards.PATTERN_FORCE_ADMIN_CB)
    )

    # Admin pending text/media input (must be registered before the URL handler)
    app.add_handler(MessageHandler(admin.pending_filter & ~filters.COMMAND, admin.handle_admin_input))
    # Public URL / text handler
    app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, handlers.handle_url))


def main() -> None:
    """Validate config, build the Application and start polling."""
    problems = config.validate_config()
    if problems:
        print("=" * 60, file=sys.stderr)
        print("❌ Configuration errors found:\n", file=sys.stderr)
        for problem in problems:
            print(f"  - {problem}", file=sys.stderr)
        print("\nFix the .env file and start the bot again.", file=sys.stderr)
        print("=" * 60, file=sys.stderr)
        sys.exit(1)

    setup_logging()
    logger.info("Starting Telegram Video Downloader Bot...")

    database = Database(config.DATABASE_PATH)

    app = (
        ApplicationBuilder()
        .token(config.BOT_TOKEN)
        .concurrent_updates(True)
        .post_init(post_init)
        .post_shutdown(post_shutdown)
        .build()
    )
    app.bot_data["db"] = database
    register_handlers(app)
    app.add_error_handler(error_handler)

    if app.job_queue is not None:
        app.job_queue.run_repeating(handlers.cleanup_job, interval=300, first=120, name="cleanup")
    else:
        logger.warning("JobQueue unavailable - periodic cleanup is disabled.")

    app.run_polling(allowed_updates=Update.ALL_TYPES, drop_pending_updates=True)


if __name__ == "__main__":
    main()
