#!/usr/bin/env python3
"""CRITICAL @abstractmethod 削除スクリプト

Phase 1.1 の CRITICAL 32件の @abstractmethod 削除を自動化。

Usage:
    python scripts/migrate/fix_critical_abstracts.py [--dry-run]
"""

import re
import sys
from pathlib import Path


def remove_abstractmethod_lines(content: str) -> tuple[str, int]:
    """@abstractmethod デコレータ行を削除

    Returns:
        (修正コンテンツ, 削除件数)
    """
    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]:
    """from abc import ABC, abstractmethod を削除

    Returns:
        (修正コンテンツ, 削除したか)
    """
    # パターン1: from abc import ABC, abstractmethod
    if re.search(r"from abc import.*\n", content):
        content = re.sub(r"from abc import [^\n]*\n", "", content)
        return content, True

    # パターン2: import abc
    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 に変更

    Returns:
        (修正コンテンツ, 変更件数)
    """
    count = 0

    # パターン1: class Foo(ABC): -> class Foo(Protocol):
    if re.search(r"class\s+\w+\s*\(\s*ABC\s*\):", content):
        content = re.sub(r"class\s+(\w+)\s*\(\s*ABC\s*\):", r"class \1(Protocol):", content)
        count += content.count("(Protocol):")

    # パターン2: class Foo(ABC, Generic[T]): -> class Foo(Protocol, Generic[T]):
    if re.search(r"class\s+\w+\s*\(\s*ABC\s*,", content):
        content = re.sub(r"class\s+(\w+)\s*\(\s*ABC\s*,", r"class \1(Protocol, ", content)
        count += 1

    return content, count


def process_file(file_path: Path, dry_run: bool = True) -> dict:
    """単一ファイルを処理

    Returns:
        処理結果の dict
    """
    try:
        content = file_path.read_text(encoding="utf-8")
    except Exception as e:
        return {"file": str(file_path), "error": str(e)}

    original = 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)

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

    return {
        "file": str(file_path),
        "abstractmethod_removed": abstract_removed,
        "abc_import_removed": abc_import_removed,
        "abc_changed": abc_changed,
        "modified": content != original,
        "dry_run": dry_run,
    }


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

    parser = argparse.ArgumentParser(
        description="CRITICAL @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

    critical_files = [
        Path("src/noveler/domain/analysis/services.py"),
        Path("src/noveler/domain/interfaces/configuration_service_protocol.py"),
        Path("src/noveler/domain/interfaces/domain_entity_factory_protocol.py"),
        Path("src/noveler/domain/interfaces/path_service_protocol.py"),
        Path("src/noveler/domain/interfaces/repository_factory_protocol.py"),
        Path("src/noveler/domain/interfaces/repository_protocol.py"),
        Path("src/noveler/domain/interfaces/stage_management_protocol.py"),
        Path("src/noveler/domain/interfaces/step_processor_factory_protocol.py"),
        Path("src/noveler/domain/protocols/serializable.py"),
    ]

    print("=" * 70)
    print("Phase 1.1: CRITICAL @abstractmethod 削除")
    print("=" * 70)
    print(f"Mode: {'DRY-RUN' if dry_run else 'APPLY'}")
    print(f"Files: {len(critical_files)}")
    print()

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

    for file_path in critical_files:
        if not file_path.exists():
            print(f"⚠️  {file_path}: Not found")
            total_changes["errors"] += 1
            continue

        result = process_file(file_path, dry_run=dry_run)

        if "error" in result:
            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}: {result['file']}")
            print(f"  - @abstractmethod removed: {result['abstractmethod_removed']}")
            print(f"  - ABC import removed: {result['abc_import_removed']}")
            print(f"  - ABC -> Protocol: {result['abc_changed']}")
            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"]
        else:
            print(f"ℹ️  {result['file']}: No changes")

    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"Modified files: {total_changes['modified_files']}/{len(critical_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())
