# src/beautyspot/__init__.py

import logging
from importlib.metadata import version, PackageNotFoundError
from pathlib import Path
from typing import Optional, Callable

from beautyspot.core import Spot as _Spot
from beautyspot.cache import CacheManager as _CacheManager

from beautyspot.types import (
    SaveErrorContext,
    PreExecuteContext,
    CacheHitContext,
    CacheMissContext,
)
from beautyspot.cachekey import KeyGen
from beautyspot.lifecycle import LifecyclePolicy, Rule, Retention
from beautyspot.limiter import TokenBucket, LimiterProtocol
from beautyspot.content_types import ContentType
from beautyspot.db import TaskDBBase, TaskDBCore, TaskDBMaintenable, SQLiteTaskDB
from beautyspot.exceptions import (
    BeautySpotError,
    CacheCorruptedError,
    SerializationError,
    ConfigurationError,
    ValidationError,
    IncompatibleProviderError,
)
from beautyspot.storage import (
    BlobStorageBase,
    BlobStorageCore,
    BlobStorageMaintenable,
    LocalStorage,
    StoragePolicyProtocol,
    WarningOnlyPolicy,
    ThresholdStoragePolicy,
    AlwaysBlobPolicy,
)
from beautyspot.serializer import SerializerProtocol, MsgpackSerializer
from beautyspot.hooks import HookBase, ThreadSafeHookBase

try:
    __version__ = version("beautyspot")
except PackageNotFoundError:
    __version__ = "0.0.0+unknown"


def Spot(
    name: str,
    db: Optional[TaskDBMaintenable] = None,
    serializer: Optional[SerializerProtocol] = None,
    limiter: Optional[LimiterProtocol] = None,
    storage_backend: Optional[BlobStorageMaintenable] = None,
    storage_policy: Optional[StoragePolicyProtocol] = None,
    cache: Optional[_CacheManager] = None,
    # --- Configuration Options ---
    lifecycle_policy: Optional[LifecyclePolicy] = None,
    eviction_rate: float = 0.0,
    blob_warning_threshold: int = 1024 * 1024,
    save_blob: bool = False,
    tpm: int = 10000,
    save_sync: bool = True,
    drain_timeout: float = 5.0,
    drain_poll_interval: float = 0.5,
    on_background_error: Optional[Callable[[BaseException, SaveErrorContext], None]] = None,
) -> _Spot:
    """
    Beautyspotのメインエントリポイント（Factory Function）。
    依存関係の解決とデフォルト設定の適用を行います。
    """

    # 0. デフォルトパス使用時のみワークスペースをセットアップ
    _default_workspace = Path(".beautyspot")

    # 1. コンポーネントの解決 (DI)
    resolved_db = db or SQLiteTaskDB(_default_workspace / f"{name}.db")
    resolved_ser = serializer or MsgpackSerializer()
    resolved_stg = storage_backend or LocalStorage(_default_workspace / "blobs" / name)
    resolved_limiter = limiter or TokenBucket(tokens_per_minute=tpm)

    # 2. Storage Policy の解決
    resolved_policy: StoragePolicyProtocol
    if storage_policy is not None:
        resolved_policy = storage_policy
    elif save_blob:
        resolved_policy = AlwaysBlobPolicy()
    else:
        logger = logging.getLogger("beautyspot")
        resolved_policy = WarningOnlyPolicy(
            warning_threshold=blob_warning_threshold, logger=logger
        )

    # 3. CacheManager の組み立て (Composition)
    resolved_cache = cache or _CacheManager(
        db=resolved_db,
        storage=resolved_stg,
        serializer=resolved_ser,
        storage_policy=resolved_policy,
        lifecycle_policy=lifecycle_policy,
    )

    # 4. Core Spot の生成
    spot = _Spot(
        name=name,
        cache=resolved_cache,
        limiter=resolved_limiter,
        # その他のオプション
        save_sync=save_sync,
        eviction_rate=eviction_rate,
        drain_timeout=drain_timeout,
        drain_poll_interval=drain_poll_interval,
        on_background_error=on_background_error,
    )

    # ファクトリ関数で内部的に DB を生成した場合のみ、GC 時にシャットダウンする。
    # ユーザーが明示的に db= を渡した場合はライフサイクルを呼び出し元に委ねる。
    if db is None:
        spot._owns_db = True

    return spot


# isinstance(spot, bs.SpotType) のための型エクスポート
SpotType: type[_Spot] = _Spot

__all__ = [
    # --- Core ---
    "Spot",
    "SpotType",
    "KeyGen",
    "ContentType",
    "SaveErrorContext",
    # --- Exceptions ---
    "BeautySpotError",
    "CacheCorruptedError",
    "SerializationError",
    "ConfigurationError",
    "ValidationError",
    "IncompatibleProviderError",
    # --- Protocols & Base Classes (for custom implementations) ---
    "TaskDBCore",
    "TaskDBMaintenable",
    "TaskDBBase",
    "BlobStorageCore",
    "BlobStorageMaintenable",
    "BlobStorageBase",
    "SerializerProtocol",
    "StoragePolicyProtocol",
    "LimiterProtocol",
    # --- Default Implementations ---
    "SQLiteTaskDB",
    "LocalStorage",
    "MsgpackSerializer",
    "TokenBucket",
    "ThresholdStoragePolicy",
    "WarningOnlyPolicy",
    "AlwaysBlobPolicy",
    # --- Lifecycle ---
    "LifecyclePolicy",
    "Rule",
    "Retention",
    # --- Hooks ---
    "HookBase",
    "ThreadSafeHookBase",
    "PreExecuteContext",
    "CacheHitContext",
    "CacheMissContext",
]
