"""テンプレート: Protocol定義の正しい書き方

このファイルは、Protocol定義時に参照すべきテンプレートです。
実装時は以下の構造に従ってください。

作成日: 2025-10-24
対象: noveler型注釈パターン再設計
"""

# ============================================================================
# パターン1: シンプルなProtocol定義（推奨）
# ============================================================================

from typing import Protocol


class SimpleRepository(Protocol):
    """シンプルなリポジトリプロトコル

    Purpose:
        データストア操作の基本インターフェースを定義

    Key points:
        - 構造的部分型（ダックタイピング）を使用
        - 継承が不要なため、モック作成が簡単
        - mypy strict mode対応
    """

    def find_by_id(self, id: int) -> object | None:
        """IDで検索

        Args:
            id: 検索ID

        Returns:
            見つかったオブジェクト、未検出時はNone
        """
        ...

    def save(self, entity: object) -> None:
        """オブジェクトを保存

        Args:
            entity: 保存するオブジェクト
        """
        ...


# ============================================================================
# パターン2: Generic Protocolを使用（推奨）
# ============================================================================

from typing import TypeVar, Generic

T = TypeVar('T')
ID = TypeVar('ID')


class GenericRepository(Protocol, Generic[T, ID]):
    """汎用リポジトリプロトコル

    Purpose:
        任意のエンティティ型に対応する汎用リポジトリインターフェース

    Generics:
        T: エンティティ型
        ID: ID型

    Key points:
        - Generic型パラメータで型安全性を確保
        - 複数の具体的なリポジトリProtocolベースとなる
        - DRY原則に従う
    """

    def find_by_id(self, id: ID) -> T | None:
        """IDでエンティティを検索

        Args:
            id: 検索ID

        Returns:
            見つかったエンティティ、未検出時はNone
        """
        ...

    def save(self, entity: T) -> None:
        """エンティティを保存

        Args:
            entity: 保存するエンティティ
        """
        ...

    def delete(self, id: ID) -> None:
        """エンティティを削除

        Args:
            id: 削除するエンティティID
        """
        ...

    def find_all(self) -> list[T]:
        """全エンティティを取得

        Returns:
            エンティティのリスト
        """
        ...


# ============================================================================
# パターン3: Runtime型チェックが必要な場合のみ@runtime_checkable
# ============================================================================

from typing import runtime_checkable


@runtime_checkable
class Serializable(Protocol):
    """シリアライズ可能なオブジェクトのマーカー

    Purpose:
        ランタイムで型をチェックする必要がある場合（例：動的ディスパッチ）

    When to use @runtime_checkable:
        - isinstance(obj, Serializable) での型チェックが必要
        - ランタイムプラグインシステム
        - 動的機能検出
    """

    def to_json(self) -> str:
        """オブジェクトをJSON化

        Returns:
            JSON文字列
        """
        ...

    def from_json(self, json_str: str) -> None:
        """JSONからオブジェクトを再構築

        Args:
            json_str: JSON文字列
        """
        ...


# ============================================================================
# パターン4: ❌ 避けるべきアンチパターン
# ============================================================================

# ❌ Bad: Protocol内での@abstractmethod使用
class BadProtocolWithAbstractmethod(Protocol):
    """❌ アンチパターン: @abstractmethodを使用"""

    from abc import abstractmethod

    @abstractmethod  # ← これは不要で、むしろ混乱を招く
    def do_something(self) -> None:
        """メソッド説明"""
        pass  # ← passも不要


# ❌ Bad: Protocol内での実装
class BadProtocolWithImplementation(Protocol):
    """❌ アンチパターン: Protocol内に実装を含める"""

    def process(self, data: dict) -> dict:
        """プロセス実行"""
        # 複雑なロジックをProtocolに含めてはいけない
        result = {}
        for key, value in data.items():
            result[key] = str(value).upper()
        return result


# ============================================================================
# パターン5: ✅ テスト時のモック作成（推奨パターン）
# ============================================================================

# テスト用モック（ABC継承不要）
class MockRepository:
    """テスト用モックリポジトリ

    Protocol定義を使用することで、継承が不要となり、
    シンプルで読みやすいモック実装が可能
    """

    def __init__(self) -> None:
        self.data: dict[int, object] = {}

    def find_by_id(self, id: int) -> object | None:
        """IDで検索"""
        return self.data.get(id)

    def save(self, entity: object) -> None:
        """保存（テスト用簡易実装）"""
        if hasattr(entity, 'id'):
            self.data[entity.id] = entity  # type: ignore

    def delete(self, id: int) -> None:
        """削除"""
        self.data.pop(id, None)

    def find_all(self) -> list[object]:
        """全取得"""
        return list(self.data.values())


# 型チェック時にMockRepositoryがGenericRepositoryを満たすことを確認
mock_repo: GenericRepository = MockRepository()  # ✅ OK


# ============================================================================
# パターン6: Variance（型の共変性・反変性）
# ============================================================================

from typing import TypeVar

T_co = TypeVar('T_co', covariant=True)  # 共変：読み出し専用
T_contra = TypeVar('T_contra', contravariant=True)  # 反変：書き込み専用


class Producer(Protocol, Generic[T_co]):
    """生産者プロトコル（共変）

    Purpose:
        データを生成するインターフェース

    Covariant:
        T_co: 読み出し専用の型パラメータ
    """

    def produce(self) -> T_co:
        """データを生成

        Returns:
            生成されたデータ
        """
        ...


class Consumer(Protocol, Generic[T_contra]):
    """消費者プロトコル（反変）

    Purpose:
        データを処理するインターフェース

    Contravariant:
        T_contra: 書き込み専用の型パラメータ
    """

    def consume(self, item: T_contra) -> None:
        """データを処理

        Args:
            item: 処理するデータ
        """
        ...


# ============================================================================
# チェックリスト: Protocol定義時に確認すべき項目
# ============================================================================

"""
Protocol定義時は、以下をチェックしてください：

□ @abstractmethod デコレータを使用していない
□ メソッド本体が ... のみである
□ クラス定義が Protocol を継承している
□ docstring で Purpose/Args/Returns を明記している
□ @runtime_checkable は本当に必要か確認した
□ Generic型パラメータが正確に設定されている（必要に応じて）
□ mypy --strict で検証可能か確認した
□ 既存Protocol/ABCとの重複がない
□ テスト時のモック作成が簡単か確認した
□ 実装クラスがProtocolを満たすことを確認した

チェック後、このテンプレートをベースに実装してください。
"""

# ============================================================================
# 参考資料
# ============================================================================

"""
詳細なガイドは以下を参照：
- docs/design/protocol_guidelines.md: Protocol設計ガイドライン
- docs/migration/abc_to_protocol_migration.md: マイグレーション手順
- PEP 544: https://peps.python.org/pep-0544/
"""
