#!/usr/bin/env python3
"""Phase 4: Virtual Repository Modules の @abstractmethod 削除スクリプト

Learning/Writing/Deployment等の特殊なRepository実装パターンに対応
__init_subclass__メタクラスカスタマイズを保持しながらProtocol化

Usage:
    python scripts/migrate/fix_virtual_repository_abstracts.py [--dry-run]
    python scripts/migrate/fix_virtual_repository_abstracts.py --apply

Options:
    --dry-run       ドライランモード（デフォルト）
    --apply         ファイルに変更を適用
"""

import re
import sys
from pathlib import Path
from typing import NamedTuple


class FileProcessResult(NamedTuple):
    """ファイル処理結果"""
    file: str
    abstractmethod_removed: int
    abc_import_removed: bool
    abc_changed: int
    protocol_import_added: bool
    init_subclass_preserved: bool
    modified: bool
    error: str | None = None


def remove_abstractmethod_lines(content: str) -> tuple[str, int]:
    """@abstractmethod デコレータ行を削除"""
    pattern = r"^\s*@abstractmethod\s*\n"
    matches = len(re.findall(pattern, content, re.MULTILINE))
    modified = re.sub(pattern, "", content, flags=re.MULTILINE)
    return modified, matches


def remove_abc_import(content: str) -> tuple[str, bool]:
    """ABC import を削除"""
    if re.search(r"from abc import.*\n", content):
        content = re.sub(r"from abc import [^\n]*\n", "", content)
        return content, True
    if "import abc\n" in content:
        content = content.replace("import abc\n", "")
        return content, True
    return content, False


def change_abc_to_protocol(content: str) -> tuple[str, int]:
    """ABC を Protocol に変更"""
    count = 0
    matches = re.findall(r"class\s+\w+\s*\(\s*ABC\s*\):", content)
    if matches:
        content = re.sub(r"class\s+(\w+)\s*\(\s*ABC\s*\):", r"class \1(Protocol):", content)
        count += len(matches)
    matches = re.findall(r"class\s+\w+\s*\(\s*ABC\s*,", content)
    if matches:
        content = re.sub(r"class\s+(\w+)\s*\(\s*ABC\s*,", r"class \1(Protocol, ", content)
        count += len(matches)
    return content, count


def add_protocol_import(content: str) -> tuple[str, bool]:
    """Protocol import を追加"""
    if "Protocol" in content and re.search(r"from typing import.*Protocol", content, re.DOTALL):
        return content, False
    if "class " in content and "Protocol)" not in content:
        return content, False
    if re.search(r"from typing import", content):
        pattern = r"from typing import ([^(\n]*)"
        replacement = lambda m: f"from typing import Protocol, {m.group(1)}" if "Protocol" not in m.group(1) else m.group(0)
        new_content = re.sub(pattern, replacement, content, count=1)
        if new_content != content:
            return new_content, True
    return content, False


def preserve_init_subclass(content: str) -> bool:
    """__init_subclass__ メタクラスカスタマイズが保持されているか確認"""
    return bool(re.search(r"def __init_subclass__\(", content))


def process_file(file_path: Path, dry_run: bool = True) -> FileProcessResult:
    """単一ファイルを処理"""
    try:
        content = file_path.read_text(encoding="utf-8")
    except Exception as e:
        return FileProcessResult(
            file=str(file_path),
            abstractmethod_removed=0,
            abc_import_removed=False,
            abc_changed=0,
            protocol_import_added=False,
            init_subclass_preserved=False,
            modified=False,
            error=str(e),
        )

    original = content

    # __init_subclass__ の保存確認
    has_init_subclass = preserve_init_subclass(content)

    # 1. @abstractmethod 削除
    content, abstract_removed = remove_abstractmethod_lines(content)

    # 2. ABC import 削除
    content, abc_import_removed = remove_abc_import(content)

    # 3. ABC -> Protocol 変更
    content, abc_changed = change_abc_to_protocol(content)

    # 4. Protocol import 追加
    content, protocol_import_added = add_protocol_import(content)

    # ファイルに書き込み（dry_run でなければ）
    if not dry_run and content != original:
        file_path.write_text(content, encoding="utf-8")

    return FileProcessResult(
        file=str(file_path),
        abstractmethod_removed=abstract_removed,
        abc_import_removed=abc_import_removed,
        abc_changed=abc_changed,
        protocol_import_added=protocol_import_added,
        init_subclass_preserved=has_init_subclass,
        modified=content != original,
        error=None,
    )


def main() -> int:
    """メインエントリーポイント"""
    import argparse

    parser = argparse.ArgumentParser(
        description="Phase 4: Virtual Repository Modules の @abstractmethod 削除スクリプト"
    )
    parser.add_argument(
        "--dry-run",
        action="store_true",
        default=True,
        help="ドライランモード（デフォルト: True）",
    )
    parser.add_argument(
        "--apply",
        action="store_true",
        help="実際にファイルに適用（--dry-run を override）",
    )
    args = parser.parse_args()

    dry_run = not args.apply

    # Virtual Repository ファイルを収集
    domain_dir = Path("src/noveler/domain")
    target_files = []

    virtual_repo_patterns = [
        "learning/repositories.py",
        "writing/repositories.py",
        "deployment/repositories.py",
        "specification/repositories.py",
        "initialization/repositories.py",
        "quality/repositories.py",
        "error_messages/repositories.py",
        "value_objects/quality_standards.py",
        "entities/quality_checker_strategy.py",
    ]

    for pattern in virtual_repo_patterns:
        file_path = domain_dir / pattern
        if file_path.exists():
            content = file_path.read_text(encoding="utf-8")
            if "@abstractmethod" in content:
                target_files.append(file_path)

    print("=" * 70)
    print("Phase 4: Virtual Repository Modules の @abstractmethod 削除")
    print("=" * 70)
    print(f"Mode: {'DRY-RUN' if dry_run else 'APPLY'}")
    print(f"Files to process: {len(target_files)}")
    print()

    total_changes = {
        "abstractmethod_removed": 0,
        "abc_import_removed": 0,
        "abc_changed": 0,
        "protocol_import_added": 0,
        "init_subclass_preserved": 0,
        "modified_files": 0,
        "errors": 0,
    }

    for file_path in sorted(target_files):
        result = process_file(file_path, dry_run=dry_run)

        if result.error:
            print(f"❌ {result.file}: {result.error}")
            total_changes["errors"] += 1
        elif result.modified:
            status = "✅ Modified" if not dry_run else "ℹ️  Would modify"
            print(f"{status}: {file_path.relative_to(domain_dir)}")
            if result.abstractmethod_removed > 0:
                print(f"  - @abstractmethod removed: {result.abstractmethod_removed}")
            if result.abc_import_removed:
                print("  - ABC import removed")
            if result.abc_changed > 0:
                print(f"  - ABC -> Protocol: {result.abc_changed}")
            if result.protocol_import_added:
                print("  - Protocol import added")
            if result.init_subclass_preserved:
                print("  - __init_subclass__ preserved: ✓")
            total_changes["modified_files"] += 1
            total_changes["abstractmethod_removed"] += result.abstractmethod_removed
            total_changes["abc_import_removed"] += int(result.abc_import_removed)
            total_changes["abc_changed"] += result.abc_changed
            total_changes["protocol_import_added"] += int(result.protocol_import_added)
            total_changes["init_subclass_preserved"] += int(result.init_subclass_preserved)

    print()
    print("=" * 70)
    print("Summary")
    print("=" * 70)
    print(f"@abstractmethod removed: {total_changes['abstractmethod_removed']}")
    print(f"ABC import removed: {total_changes['abc_import_removed']}")
    print(f"ABC -> Protocol changed: {total_changes['abc_changed']}")
    print(f"Protocol import added: {total_changes['protocol_import_added']}")
    print(f"__init_subclass__ preserved: {total_changes['init_subclass_preserved']}")
    print(f"Modified files: {total_changes['modified_files']}/{len(target_files)}")
    print(f"Errors: {total_changes['errors']}")
    print()

    if dry_run:
        print("ℹ️  This is a dry-run. Use --apply to apply changes.")
    else:
        print("✅ Changes applied.")

    return 0 if total_changes["errors"] == 0 else 1


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