"""Process entry point: configuration, lifecycle and polling."""

from __future__ import annotations

import argparse
import asyncio
import json
import logging
import shutil
import sys

from aiogram.exceptions import TelegramAPIError, TelegramNetworkError, TelegramUnauthorizedError
from aiogram.types import BotCommand
from pydantic import ValidationError

from app.bot import build_dispatcher, create_bot
from app.config import Settings
from app.context import AppContext, build_context
from app.utils.logging_config import setup_logging

logger = logging.getLogger(__name__)

BOT_COMMANDS = [
    BotCommand(command="start", description="شروع و منوی اصلی"),
    BotCommand(command="help", description="راهنما و قوانین"),
    BotCommand(command="add", description="افزودن آدرس جدید"),
    BotCommand(command="status", description="وضعیت ربات یا یک عملیات"),
    BotCommand(command="jobs", description="فهرست عملیات‌ها"),
    BotCommand(command="pause", description="توقف موقت عملیات"),
    BotCommand(command="resume", description="ادامه عملیات متوقف‌شده"),
    BotCommand(command="cancel", description="لغو عملیات"),
    BotCommand(command="settings", description="تنظیم مقصد و محدودیت‌ها"),
    BotCommand(command="history", description="تاریخچه ارسال‌ها"),
    BotCommand(command="id", description="نمایش شناسه کاربر و چت"),
    BotCommand(command="admin", description="عیب‌یابی مدیر"),
]


def _configure_stdio() -> None:
    """Use UTF-8 for console output even when Windows pipes default to a legacy code page."""

    for stream in (sys.stdout, sys.stderr):
        reconfigure = getattr(stream, "reconfigure", None)
        if reconfigure is None:
            continue
        try:
            reconfigure(encoding="utf-8", errors="replace")
        except (OSError, ValueError):
            # A replaced stream (tests, embedded terminals) may not be reconfigurable.
            pass


def _load_settings() -> Settings:
    return Settings()


def _print_validation_error(exc: ValidationError) -> None:
    print("پیکربندی نامعتبر است / Invalid configuration:", file=sys.stderr)
    try:
        errors = exc.errors(include_url=False, include_input=False)
    except TypeError:  # pragma: no cover - older pydantic fallback
        errors = exc.errors()
    for item in errors:
        location = ".".join(str(part) for part in item.get("loc", ())) or "config"
        message = item.get("msg", "invalid value")
        print(f"  - {location}: {message}", file=sys.stderr)
    print(
        "\nفایل .env را از روی .env.example بسازید و BOT_TOKEN، ADMIN_IDS و "
        "ALLOWED_DOMAINS را تنظیم کنید.",
        file=sys.stderr,
    )


def _check_config(settings: Settings) -> None:
    summary = settings.safe_summary()
    summary["ffmpeg_available"] = bool(shutil.which("ffmpeg") and shutil.which("ffprobe"))
    print(json.dumps(summary, ensure_ascii=False, indent=2, default=str))
    print("\n✅ پیکربندی از نظر ساختاری معتبر است.")
    print(
        "توجه: این بررسی اتصال به تلگرام، دسترسی کانال، اینترنت سایت یا "
        "نصب Chromium/FFmpeg را آزمون نمی‌کند."
    )


async def _set_commands(bot) -> None:  # type: ignore[no-untyped-def]
    try:
        await bot.set_my_commands(BOT_COMMANDS)
    except TelegramAPIError:
        logger.warning("could not register bot command descriptions", exc_info=True)


async def _run(settings: Settings) -> None:
    bot = create_bot(settings)
    context: AppContext | None = None
    try:
        try:
            me = await bot.get_me()
        except TelegramUnauthorizedError:
            logger.error("BOT_TOKEN was rejected by Telegram")
            return
        except TelegramNetworkError:
            logger.error("could not reach the Telegram Bot API")
            return

        context = await build_context(settings, bot)
        dispatcher = build_dispatcher(context)
        await context.jobs.start()
        await _set_commands(bot)
        logger.info(
            "bot @%s is running; destination configured=%s, workers=%s",
            me.username or me.id,
            bool(await context.repo.get_setting("destination_chat_id"))
            or settings.default_chat_id is not None,
            settings.max_concurrent_downloads,
        )
        await dispatcher.start_polling(
            bot,
            allowed_updates=dispatcher.resolve_used_update_types(),
            close_bot_session=False,
            handle_signals=True,
        )
    finally:
        if context is not None:
            await context.close()
        try:
            await bot.session.close()
        except Exception:
            logger.debug("closing bot session failed", exc_info=True)


def cli() -> None:
    _configure_stdio()
    parser = argparse.ArgumentParser(
        description="Telegram video crawler/publisher bot",
    )
    parser.add_argument(
        "--check-config",
        action="store_true",
        help="validate .env and print a non-secret summary, then exit",
    )
    args = parser.parse_args()

    try:
        settings = _load_settings()
    except ValidationError as exc:
        _print_validation_error(exc)
        raise SystemExit(2) from None

    if args.check_config:
        _check_config(settings)
        return

    setup_logging(settings.log_level, settings.log_file)
    try:
        asyncio.run(_run(settings))
    except KeyboardInterrupt:
        logger.info("stopped by keyboard interrupt")
    except Exception:
        logger.exception("fatal startup/runtime error")
        raise SystemExit(1) from None


if __name__ == "__main__":
    cli()
