# Issue #02: 設定プロバイダーレジストリ

**Phase:** 1a
**優先度:** 高
**依存関係:** #01
**見積もり:** 0.5日

---

## 概要

フレームワークとユーザーコードを分離するための設定プロバイダーレジストリを実装する。
これにより、@nodeデコレータがユーザーの設定にアクセスできるようになる。

---

## TDD実装手順

### Step 1: Red（テストを書く）

```python
# tests/unit/core/test_config.py
"""Tests for configuration provider registry."""
import pytest
from unittest.mock import Mock


class TestSettingsProviderRegistry:
    """Test settings provider registration and retrieval."""

    def test_initial_provider_is_none(self):
        """Provider should be None initially."""
        from railway.core.config import get_settings_provider
        # Reset state first
        from railway.core import config
        config._settings_provider = None

        assert get_settings_provider() is None

    def test_register_settings_provider(self):
        """Should be able to register a settings provider."""
        from railway.core.config import (
            register_settings_provider,
            get_settings_provider,
        )
        from railway.core import config
        config._settings_provider = None

        mock_provider = Mock(return_value={"key": "value"})
        register_settings_provider(mock_provider)

        assert get_settings_provider() is mock_provider

    def test_get_retry_config_without_provider(self):
        """Should return default settings when no provider."""
        from railway.core.config import get_retry_config
        from railway.core import config
        config._settings_provider = None

        retry_config = get_retry_config("test_node")

        assert retry_config.max_attempts == 3
        assert retry_config.min_wait == 2
        assert retry_config.max_wait == 10
        assert retry_config.multiplier == 1

    def test_get_retry_config_with_provider(self):
        """Should use provider settings when registered."""
        from railway.core.config import (
            register_settings_provider,
            get_retry_config,
        )
        from railway.core import config
        config._settings_provider = None

        # Create mock settings with get_retry_settings method
        mock_settings = Mock()
        mock_retry = Mock()
        mock_retry.max_attempts = 5
        mock_retry.min_wait = 1
        mock_retry.max_wait = 30
        mock_retry.multiplier = 2
        mock_settings.get_retry_settings.return_value = mock_retry

        register_settings_provider(lambda: mock_settings)

        retry_config = get_retry_config("fetch_data")

        assert retry_config.max_attempts == 5
        mock_settings.get_retry_settings.assert_called_once_with("fetch_data")

    def test_get_retry_config_provider_error(self):
        """Should return default when provider raises exception."""
        from railway.core.config import (
            register_settings_provider,
            get_retry_config,
        )
        from railway.core import config
        config._settings_provider = None

        def failing_provider():
            raise RuntimeError("Config error")

        register_settings_provider(failing_provider)

        retry_config = get_retry_config("test_node")

        # Should fallback to defaults
        assert retry_config.max_attempts == 3


class TestDefaultRetrySettings:
    """Test default retry settings."""

    def test_default_values(self):
        """Should have correct default values."""
        from railway.core.config import DefaultRetrySettings

        settings = DefaultRetrySettings()

        assert settings.max_attempts == 3
        assert settings.min_wait == 2
        assert settings.max_wait == 10
        assert settings.multiplier == 1
```

```bash
# 実行して失敗を確認
pytest tests/unit/core/test_config.py -v
# Expected: FAILED (ImportError)
```

### Step 2: Green（最小限の実装）

```python
# railway/core/config.py
"""
Settings provider registry for framework-user code separation.

This module allows the @node decorator to access user settings
without directly importing user code.
"""
from typing import Callable, Any, Protocol


class RetrySettingsProtocol(Protocol):
    """Protocol for retry settings objects."""
    max_attempts: int
    min_wait: int
    max_wait: int
    multiplier: int


# Global settings provider
_settings_provider: Callable[[], Any] | None = None


def register_settings_provider(provider: Callable[[], Any]) -> None:
    """
    Register a settings provider function.

    The provider should return a settings object with get_retry_settings() method.

    Args:
        provider: A callable that returns the settings object

    Example:
        from railway.core.config import register_settings_provider
        from src.settings import get_settings

        register_settings_provider(get_settings)
    """
    global _settings_provider
    _settings_provider = provider


def get_settings_provider() -> Callable[[], Any] | None:
    """
    Get the registered settings provider.

    Returns:
        The registered provider function, or None if not registered.
    """
    return _settings_provider


class DefaultRetrySettings:
    """
    Default retry settings when no provider is registered.

    Used as fallback when:
    - No settings provider is registered
    - The provider raises an exception
    """
    max_attempts: int = 3
    min_wait: int = 2
    max_wait: int = 10
    multiplier: int = 1


def get_retry_config(node_name: str) -> RetrySettingsProtocol:
    """
    Get retry configuration for a specific node.

    If no settings provider is registered, returns default settings.

    Args:
        node_name: Name of the node to get settings for

    Returns:
        Retry configuration object with max_attempts, min_wait, max_wait, multiplier
    """
    if _settings_provider is None:
        return DefaultRetrySettings()

    try:
        settings = _settings_provider()
        return settings.get_retry_settings(node_name)
    except Exception:
        return DefaultRetrySettings()


def reset_provider() -> None:
    """
    Reset the settings provider (for testing).
    """
    global _settings_provider
    _settings_provider = None
```

```bash
# 実行して成功を確認
pytest tests/unit/core/test_config.py -v
# Expected: PASSED
```

### Step 3: Refactor

- Protocolを使った型安全性の向上
- ドキュメントの充実

---

## 完了条件

- [ ] `register_settings_provider()` が動作する
- [ ] `get_settings_provider()` が登録されたプロバイダーを返す
- [ ] `get_retry_config()` がプロバイダーから設定を取得する
- [ ] プロバイダーがない場合はデフォルト設定を返す
- [ ] プロバイダーがエラーを起こした場合もデフォルト設定を返す
- [ ] テストカバレッジ90%以上

---

## 次のIssue

- #03: @nodeデコレータ（基本版）
