"""Application composition root and shared runtime dependencies."""

from __future__ import annotations

import logging
from dataclasses import dataclass, field
from datetime import UTC, datetime

from aiogram import Bot
from aiogram.exceptions import TelegramAPIError

from app.adapters.browser_adapter import BrowserRenderer
from app.adapters.registry import AdapterRegistry, build_default_registry
from app.config import Settings
from app.database.database import Database
from app.database.repository import Repository
from app.services.crawler import Crawler
from app.services.downloader import Downloader
from app.services.extractor import Extractor
from app.services.http_client import SafeHttpClient
from app.services.job_manager import JobManager
from app.services.media_processor import MediaProcessor
from app.services.uploader import Uploader
from app.services.url_validator import NetworkGuard

logger = logging.getLogger(__name__)


@dataclass(slots=True)
class AppContext:
    settings: Settings
    bot: Bot
    database: Database
    repo: Repository
    guard: NetworkGuard
    http: SafeHttpClient
    browser: BrowserRenderer
    registry: AdapterRegistry
    extractor: Extractor
    processor: MediaProcessor
    crawler: Crawler
    downloader: Downloader
    uploader: Uploader
    jobs: JobManager
    started_at: datetime = field(default_factory=lambda: datetime.now(UTC))
    _closed: bool = False

    async def close(self) -> None:
        if self._closed:
            return
        self._closed = True
        try:
            await self.jobs.stop()
        except Exception:
            logger.exception("could not stop the job manager cleanly")
        try:
            await self.browser.close()
        except Exception:
            logger.exception("could not close the browser cleanly")
        try:
            await self.http.close()
        except Exception:
            logger.exception("could not close the HTTP client cleanly")
        try:
            await self.database.close()
        except Exception:
            logger.exception("could not close the database cleanly")


async def build_context(settings: Settings, bot: Bot) -> AppContext:
    settings.ensure_directories()

    database = Database(settings.database_path)
    await database.connect()
    repo = Repository(database)
    guard = NetworkGuard(settings.allowed_domains)
    http = SafeHttpClient(
        guard,
        timeout=settings.request_timeout,
        user_agent=settings.http_user_agent,
        proxy=settings.proxy_url,
    )
    await http.start()
    browser = BrowserRenderer(
        guard,
        headless=settings.playwright_headless,
        user_agent=settings.http_user_agent,
        timeout=settings.request_timeout,
        max_html_bytes=settings.max_html_bytes,
    )
    registry = build_default_registry()
    extractor = Extractor(guard)
    processor = MediaProcessor(enabled=settings.ffmpeg_enabled)
    downloader = Downloader(settings, guard, http)
    uploader = Uploader(settings, bot, repo, http)
    crawler = Crawler(settings, guard, http, browser, extractor, registry, repo)

    async def notify(chat_id: int, text: str) -> None:
        try:
            await bot.send_message(
                chat_id=chat_id,
                text=text,
                parse_mode="HTML",
                disable_web_page_preview=True,
            )
        except TelegramAPIError:
            logger.warning("progress notification could not be delivered")

    jobs = JobManager(
        settings,
        repo,
        crawler,
        downloader,
        uploader,
        notifier=notify,
    )
    return AppContext(
        settings=settings,
        bot=bot,
        database=database,
        repo=repo,
        guard=guard,
        http=http,
        browser=browser,
        registry=registry,
        extractor=extractor,
        processor=processor,
        crawler=crawler,
        downloader=downloader,
        uploader=uploader,
        jobs=jobs,
    )
