# scraper2_hj3415/app/composition.py
from __future__ import annotations

from dataclasses import dataclass

from db2_hj3415.common.types import MongoDoc
from db2_hj3415.mongo import Mongo
from db2_hj3415.nfs.repo import ensure_indexes
from db2_hj3415.settings import Settings as Db2Settings
from pymongo.asynchronous.database import AsyncDatabase
from scraper2_hj3415.app.dart.composition import (
    DartUsecases,
    build_dart_usecases,
)
from scraper2_hj3415.app.nfs.composition import (
    NfsUsecases,
    build_nfs_usecases,
)
from scraper2_hj3415.app.nfs.domain.types import Sink
from scraper2_hj3415.app.settings import Settings, get_settings


def build_db2_settings(settings: Settings) -> Db2Settings:
    """
    scraper2 settings -> db2 settings 변환.
    환경 소유권은 scraper2가 가지고, db2는 주입받아 사용한다.
    """
    return Db2Settings(
        MONGO_URI=settings.MONGO_URI,
        MONGO_DB_NAME=settings.MONGO_DB_NAME,
        MONGO_CONNECT_TIMEOUT_MS=settings.MONGO_CONNECT_TIMEOUT_MS,
        MONGO_SERVER_SELECTION_TIMEOUT_MS=settings.MONGO_SERVER_SELECTION_TIMEOUT_MS,
        SNAPSHOT_TTL_DAYS=settings.SNAPSHOT_TTL_DAYS,
    )


@dataclass(frozen=True)
class AppResources:
    """
    앱 전체 공용 리소스 묶음.

    정책:
    - Mongo / DB는 루트 composition 에서 한 번만 생성한다.
    - nfs, dart 모두 같은 DB를 공유한다.
    - bootstrap 책임(인덱스 보장)도 여기서 가진다.
    - close 책임도 여기서 가진다.
    """

    mongo: Mongo
    db: AsyncDatabase[MongoDoc]
    db_settings: Db2Settings

    async def bootstrap(self) -> None:
        """
        앱 실행 전 필요한 인프라 초기화.
        현재는 NFS 인덱스 보장을 수행한다.
        """
        await ensure_indexes(
            self.db,
            snapshot_ttl_days=self.db_settings.SNAPSHOT_TTL_DAYS,
        )

    async def aclose(self) -> None:
        await self.mongo.close()


@dataclass(frozen=True)
class AppUsecases:
    """
    앱 전체 기능 묶음.
    """

    nfs: NfsUsecases
    dart: DartUsecases
    resources: AppResources


def build_app_resources(settings: Settings) -> AppResources:
    """
    앱 전체에서 공유할 리소스를 조립한다.
    """
    db2_settings = build_db2_settings(settings)
    mongo = Mongo(settings=db2_settings)
    db = mongo.get_db()

    return AppResources(
        mongo=mongo,
        db=db,
        db_settings=db2_settings,
    )


def build_usecases(
    *,
    settings: Settings | None = None,
    nfs_sink: Sink | None = None,
) -> AppUsecases:
    """
    앱 전체 composition root.

    책임:
    - settings 로딩
    - 공용 리소스(Mongo/DB) 생성
    - 기능별 composition(nfs, dart)에 필요한 값만 명시적으로 전달
    - 최종 AppUsecases 반환
    """
    s = settings or get_settings()
    resolved_nfs_sink: Sink = nfs_sink or s.nfs_sink

    resources = build_app_resources(s)

    nfs = build_nfs_usecases(
        sink=resolved_nfs_sink,
        headless=s.scraper_headless,
        timeout_ms=s.scraper_timeout_ms,
        max_concurrency=s.scraper_max_concurrency,
        db=resources.db,
    )

    dart = build_dart_usecases(
        dart_api_key=s.dart_api_key,
        db=resources.db,
    )

    return AppUsecases(
        nfs=nfs,
        dart=dart,
        resources=resources,
    )
