#!/usr/bin/env python3
"""ABC→Protocol 自動マイグレーションスクリプト

File: scripts/migration/abc_to_protocol_migration.py
Purpose: ABC定義ファイルをProtocol定義に自動変換
Context: noveler型注釈パターン再設計における自動化ツール
"""

import argparse
import re
import sys
from pathlib import Path


class ABCToProtocolConverter:
    """ABC→Protocolコンバーター"""

    def __init__(self, dry_run: bool = True) -> None:
        """初期化

        Args:
            dry_run: ドライランモード（ファイルを変更しない）
        """
        self.dry_run = dry_run
        self.changes_summary = {
            "import_changes": 0,
            "abstractmethod_removals": 0,
            "class_changes": 0,
            "docstring_updates": 0,
        }

    def migrate_file(self, file_path: Path) -> str:
        """ファイルをABCからProtocolに変換

        Args:
            file_path: 変換対象ファイル

        Returns:
            変更内容のサマリー
        """
        try:
            content = file_path.read_text(encoding="utf-8")
        except (UnicodeDecodeError, OSError) as e:
            return f"❌ ファイル読み込みエラー: {e}"

        original_content = content

        # 1. Import文の変更
        content = self._migrate_imports(content)

        # 2. @abstractmethod デコレータを削除
        content = self._remove_abstractmethod_decorators(content)

        # 3. ABC継承を削除
        content = self._migrate_class_definition(content)

        # 4. メソッド本体を...に変更
        content = self._migrate_method_bodies(content)

        # 変更がない場合
        if content == original_content:
            return f"ℹ️ 変更なし: {file_path.name}"

        # ドライランの場合は変更内容を表示
        if self.dry_run:
            return self._generate_diff_preview(original_content, content, file_path)

        # 実際にファイルに書き込み
        file_path.write_text(content, encoding="utf-8")
        return f"✅ 変換完了: {file_path.name}"

    def _migrate_imports(self, content: str) -> str:
        """Import文を変更

        Before: from abc import ABC, abstractmethod
        After:  from typing import Protocol
        """
        # ABC/abstractmethod import を削除
        patterns = [
            r"from abc import .*\n",
            r"^from abc import .*$",
            r"import abc\n",
        ]

        for pattern in patterns:
            if re.search(pattern, content, re.MULTILINE):
                content = re.sub(pattern, "", content, flags=re.MULTILINE)
                self.changes_summary["import_changes"] += 1

        # Protocol importを追加（存在しない場合）
        if "from typing import Protocol" not in content and "Protocol" in content:
            # typing importを見つけて追加
            if "from typing import" in content:
                content = re.sub(
                    r"(from typing import [^\n]+)",
                    r"\1, Protocol",
                    content,
                    count=1,
                )
            else:
                # typing importがない場合は追加
                lines = content.split("\n")
                import_index = 0
                for i, line in enumerate(lines):
                    if line.startswith("from ") or line.startswith("import "):
                        import_index = i + 1
                lines.insert(import_index, "from typing import Protocol")
                content = "\n".join(lines)

        return content

    def _remove_abstractmethod_decorators(self, content: str) -> str:
        """@abstractmethodデコレータを削除

        Pattern: @abstractmethod\n    def ...
        """
        pattern = r"@abstractmethod\s*\n"
        if re.search(pattern, content):
            content = re.sub(pattern, "", content)
            self.changes_summary["abstractmethod_removals"] += content.count(
                "@abstractmethod"
            )
            content = re.sub(pattern, "", content)  # 重複削除

        return content

    def _migrate_class_definition(self, content: str) -> str:
        """クラス定義を変更

        Before: class Foo(ABC):
        After:  class Foo(Protocol):

        Before: class Foo(ABC, Generic[T]):
        After:  class Foo(Protocol, Generic[T]):
        """
        pattern = r"class\s+(\w+)\s*\(\s*ABC\s*,\s*"
        if re.search(pattern, content):
            content = re.sub(pattern, r"class \1(Protocol, ", content)
            self.changes_summary["class_changes"] += 1

        pattern = r"class\s+(\w+)\s*\(\s*ABC\s*\)\s*:"
        if re.search(pattern, content):
            content = re.sub(pattern, r"class \1(Protocol):", content)
            self.changes_summary["class_changes"] += 1

        return content

    def _migrate_method_bodies(self, content: str) -> str:
        """メソッド本体を...に変更

        Before: def foo(self):
                    pass

        After:  def foo(self):
                    ...
        """
        # パターン: メソッド定義の直後にpassがある場合
        lines = content.split("\n")
        result_lines = []
        i = 0

        while i < len(lines):
            line = lines[i]
            result_lines.append(line)

            # メソッド定義かプロパティかチェック
            if re.match(r"\s+(def|@property)\s+", line) and ":" in line:
                # 次の行がpassの場合は置換
                if i + 1 < len(lines):
                    next_line = lines[i + 1]
                    if re.match(r"\s+pass\s*$", next_line):
                        # インデント取得
                        indent = len(next_line) - len(next_line.lstrip())
                        result_lines.append(" " * indent + "...")
                        i += 2  # passをスキップ
                        self.changes_summary["docstring_updates"] += 1
                        continue

            i += 1

        return "\n".join(result_lines)

    def _generate_diff_preview(
        self, original: str, modified: str, file_path: Path
    ) -> str:
        """変更プレビューを生成

        Args:
            original: 元のコンテンツ
            modified: 変更後のコンテンツ
            file_path: ファイルパス

        Returns:
            プレビューテキスト
        """
        diff_lines = []
        original_lines = original.split("\n")
        modified_lines = modified.split("\n")

        diff_lines.append(f"\n[DRY-RUN] {file_path.name}")
        diff_lines.append("=" * 70)

        # 簡易的なdiff（変更行のみ）
        for i, (orig, mod) in enumerate(
            zip(original_lines, modified_lines, strict=False), 1
        ):
            if orig != mod:
                diff_lines.append(f"Line {i}:")
                diff_lines.append(f"  - {orig[:70]}")
                diff_lines.append(f"  + {mod[:70]}")

        if len(modified_lines) != len(original_lines):
            diff_lines.append(
                f"\nLine count: {len(original_lines)} → {len(modified_lines)}"
            )

        return "\n".join(diff_lines)

    def summary(self) -> str:
        """変更サマリーを返す"""
        return (
            f"\n変更サマリー:\n"
            f"  - Import変更: {self.changes_summary['import_changes']}\n"
            f"  - @abstractmethod削除: {self.changes_summary['abstractmethod_removals']}\n"
            f"  - クラス定義変更: {self.changes_summary['class_changes']}\n"
            f"  - メソッド本体更新: {self.changes_summary['docstring_updates']}"
        )


def main() -> int:
    """メインエントリーポイント"""
    parser = argparse.ArgumentParser(
        description="ABC→Protocol 自動マイグレーション"
    )
    parser.add_argument(
        "--file",
        "-f",
        type=Path,
        required=True,
        help="変換対象ファイルパス",
    )
    parser.add_argument(
        "--dry-run",
        action="store_true",
        help="ドライランモード（ファイルを変更しない）",
    )

    args = parser.parse_args()

    if not args.file.exists():
        print(f"❌ ファイルが見つかりません: {args.file}")
        return 1

    converter = ABCToProtocolConverter(dry_run=args.dry_run or not args.file.exists())
    result = converter.migrate_file(args.file)

    print(result)
    print(converter.summary())

    return 0


if __name__ == "__main__":
    sys.exit(main())
