"""Tools 17, 18: refactor_func, apply_refactor_func."""

from __future__ import annotations

from pathlib import Path
from typing import Any

from ..hints import generate_hints, get_session
from ..incremental import find_project_root
from ..refactor import (
    apply_refactor,
    find_dead_code,
    rename_preview,
    suggest_refactorings,
)
from ._common import _get_store, _validate_repo_root

# ---------------------------------------------------------------------------
# Tool 17: refactor_tool  [REFACTOR]
# ---------------------------------------------------------------------------


def refactor_func(
    mode: str = "rename",
    old_name: str | None = None,
    new_name: str | None = None,
    kind: str | None = None,
    file_pattern: str | None = None,
    repo_root: str | None = None,
) -> dict[str, Any]:
    """Unified refactoring entry point.

    [REFACTOR] Supports three modes:
    - ``rename``: Preview renaming a symbol (requires *old_name* and
      *new_name*).
    - ``dead_code``: Find unreferenced functions/classes.
    - ``suggest``: Get community-driven refactoring suggestions.

    Args:
        mode: One of ``"rename"``, ``"dead_code"``, or ``"suggest"``.
        old_name: (rename mode) Current symbol name.
        new_name: (rename mode) Desired new name.
        kind: (dead_code mode) Optional node kind filter.
        file_pattern: (dead_code mode) Optional file path substring filter.
        repo_root: Repository root path. Auto-detected if omitted.

    Returns:
        Mode-specific results dict.
    """
    valid_modes = {"rename", "dead_code", "suggest"}
    if mode not in valid_modes:
        return {
            "status": "error",
            "error": (
                f"Invalid mode '{mode}'. "
                f"Must be one of: {', '.join(sorted(valid_modes))}"
            ),
        }

    store, root = _get_store(repo_root)
    try:
        if mode == "rename":
            if not old_name or not new_name:
                return {
                    "status": "error",
                    "error": (
                        "rename mode requires both old_name and new_name."
                    ),
                }
            preview = rename_preview(store, old_name, new_name)
            if preview is None:
                return {
                    "status": "not_found",
                    "summary": f"No node found matching '{old_name}'.",
                }
            result = {
                "status": "ok",
                "summary": (
                    f"Rename preview: {old_name} -> {new_name}, "
                    f"{len(preview['edits'])} edit(s). "
                    f"Use apply_refactor_tool(refactor_id="
                    f"'{preview['refactor_id']}') to apply."
                ),
                **preview,
            }
            result["_hints"] = generate_hints(
                "refactor", result, get_session()
            )
            return result

        elif mode == "dead_code":
            dead = find_dead_code(
                store, kind=kind, file_pattern=file_pattern, root=root
            )
            result = {
                "status": "ok",
                "summary": f"Found {len(dead)} dead code symbol(s).",
                "dead_code": dead,
                "total": len(dead),
            }
            result["_hints"] = generate_hints(
                "refactor", result, get_session()
            )
            return result

        else:  # suggest
            suggestions = suggest_refactorings(store)
            result = {
                "status": "ok",
                "summary": (
                    f"Generated {len(suggestions)} "
                    "refactoring suggestion(s)."
                ),
                "suggestions": suggestions,
                "total": len(suggestions),
            }
            result["_hints"] = generate_hints(
                "refactor", result, get_session()
            )
            return result

    except Exception as exc:
        return {"status": "error", "error": str(exc)}
    finally:
        store.close()


# ---------------------------------------------------------------------------
# Tool 18: apply_refactor_tool  [REFACTOR]
# ---------------------------------------------------------------------------


def apply_refactor_func(
    refactor_id: str,
    repo_root: str | None = None,
    dry_run: bool = False,
) -> dict[str, Any]:
    """Apply a previously previewed refactoring to source files.

    [REFACTOR] Validates the refactor_id, checks expiry, ensures all edit
    paths are within the repo root, then performs exact string replacements.

    Args:
        refactor_id: ID returned by a prior ``refactor_tool(mode="rename")``
            call.
        repo_root: Repository root path. Auto-detected if omitted.
        dry_run: If True, return a unified diff of what would change
            without touching disk. The refactor_id remains valid so the
            user can review the diff, then call again with ``dry_run=False``
            to actually write the changes. See: #176

    Returns:
        Status with count of applied edits and modified files. When
        ``dry_run=True`` the response additionally contains ``would_modify``
        (list of file paths) and ``diffs`` (map of file -> unified-diff
        string).
    """
    try:
        root = (
            _validate_repo_root(Path(repo_root))
            if repo_root
            else find_project_root()
        )
    except (RuntimeError, ValueError) as exc:
        return {"status": "error", "error": str(exc)}

    result = apply_refactor(refactor_id, root, dry_run=dry_run)
    return result
