#!/usr/bin/env python3
"""
Pre-commit hook to enforce using pathlib.Path instead of os.path.

This hook checks for os.path usage in Python files and suggests
using pathlib.Path instead for better cross-platform compatibility
and more modern Python code.
"""

import argparse
import ast
import sys
from pathlib import Path
from typing import List, Tuple


class OsPathChecker(ast.NodeVisitor):
    """AST visitor to find os.path usage."""

    def __init__(self, filename: str):
        self.filename = filename
        self.violations: List[Tuple[int, str]] = []
        self.has_os_import = False
        self.has_os_path_import = False

    def visit_Import(self, node: ast.Import) -> None:
        """Check for 'import os' statements."""
        for alias in node.names:
            if alias.name == "os":
                self.has_os_import = True
        self.generic_visit(node)

    def visit_ImportFrom(self, node: ast.ImportFrom) -> None:
        """Check for 'from os import path' or 'from os.path import ...' statements."""
        if node.module == "os" and any(
            alias.name == "path" for alias in node.names
        ):
            self.has_os_path_import = True
            self.violations.append(
                (
                    node.lineno,
                    "Found 'from os import path' - use 'from pathlib import Path' instead",
                )
            )
        elif node.module == "os.path":
            self.has_os_path_import = True
            imported_names = [alias.name for alias in node.names]
            self.violations.append(
                (
                    node.lineno,
                    f"Found 'from os.path import {', '.join(imported_names)}' - use pathlib.Path methods instead",
                )
            )
        self.generic_visit(node)

    def visit_Attribute(self, node: ast.Attribute) -> None:
        """Check for os.path.* usage."""
        if (
            isinstance(node.value, ast.Name)
            and node.value.id == "os"
            and node.attr == "path"
            and self.has_os_import
        ):
            # This is os.path usage
            # Try to get the specific method being called
            parent = getattr(node, "parent", None)
            if parent and isinstance(parent, ast.Attribute):
                method = parent.attr
                # Skip os.path.expandvars as it has no pathlib equivalent
                if method == "expandvars":
                    return
                suggestion = get_pathlib_equivalent(f"os.path.{method}")
            else:
                suggestion = "Use pathlib.Path instead"

            self.violations.append(
                (node.lineno, f"Found os.path usage - {suggestion}")
            )
        self.generic_visit(node)

    def visit_Call(self, node: ast.Call) -> None:
        """Check for direct calls to os.path functions."""
        if isinstance(node.func, ast.Attribute):
            # Store parent reference for better context
            node.func.parent = node

            # Check for os.path.* calls
            if (
                isinstance(node.func.value, ast.Attribute)
                and isinstance(node.func.value.value, ast.Name)
                and node.func.value.value.id == "os"
                and node.func.value.attr == "path"
            ):
                method = node.func.attr
                # Skip os.path.expandvars as it has no pathlib equivalent
                if method == "expandvars":
                    return
                suggestion = get_pathlib_equivalent(f"os.path.{method}")
                self.violations.append(
                    (node.lineno, f"Found os.path.{method}() - {suggestion}")
                )
        self.generic_visit(node)


def get_pathlib_equivalent(os_path_call: str) -> str:
    """Get the pathlib equivalent for common os.path operations."""
    equivalents = {
        "os.path.join": "Use Path() / 'subpath' or Path().joinpath()",
        "os.path.exists": "Use Path().exists()",
        "os.path.isfile": "Use Path().is_file()",
        "os.path.isdir": "Use Path().is_dir()",
        "os.path.dirname": "Use Path().parent",
        "os.path.basename": "Use Path().name",
        "os.path.abspath": "Use Path().resolve()",
        "os.path.realpath": "Use Path().resolve()",
        "os.path.expanduser": "Use Path().expanduser()",
        "os.path.split": "Use Path().parent and Path().name",
        "os.path.splitext": "Use Path().stem and Path().suffix",
        "os.path.getsize": "Use Path().stat().st_size",
        "os.path.getmtime": "Use Path().stat().st_mtime",
        "os.path.normpath": "Use Path() - it normalizes automatically",
        # Note: os.path.expandvars has no pathlib equivalent and is allowed
        "os.path.expandvars": "(No pathlib equivalent - allowed)",
    }
    return equivalents.get(os_path_call, "Use pathlib.Path equivalent method")


def check_file(
    filepath: Path, allow_legacy: bool = False
) -> List[Tuple[str, int, str]]:
    """
    Check a Python file for os.path usage.

    Args:
        filepath: Path to the Python file to check
        allow_legacy: If True, only check modified lines (not implemented yet)

    Returns:
        List of (filename, line_number, violation_message) tuples
    """
    try:
        content = filepath.read_text(encoding="utf-8")
        tree = ast.parse(content, filename=str(filepath))
    except SyntaxError as e:
        print(f"Syntax error in {filepath}: {e}", file=sys.stderr)
        return []
    except Exception as e:
        print(f"Error reading {filepath}: {e}", file=sys.stderr)
        return []

    checker = OsPathChecker(str(filepath))
    checker.visit(tree)

    return [(str(filepath), line, msg) for line, msg in checker.violations]


def main() -> int:
    """Main entry point for the pre-commit hook."""
    parser = argparse.ArgumentParser(
        description="Check for os.path usage and suggest pathlib alternatives"
    )
    parser.add_argument(
        "filenames",
        nargs="*",
        help="Python files to check",
    )
    parser.add_argument(
        "--allow-legacy",
        action="store_true",
        help="Allow os.path in existing code (only check new/modified lines)",
    )

    args = parser.parse_args()

    # List of files that are allowed to use os.path (legacy or special cases)
    ALLOWED_FILES = {
        "src/local_deep_research/utilities/log_utils.py",  # May need os.path for low-level operations
        "src/local_deep_research/config/paths.py",  # Already migrated but may have legacy code
        ".pre-commit-hooks/check-pathlib-usage.py",  # This file itself
    }

    violations = []
    for filename in args.filenames:
        filepath = Path(filename)

        # Skip non-Python files
        if not filename.endswith(".py"):
            continue

        # Skip allowed files
        if any(filename.endswith(allowed) for allowed in ALLOWED_FILES):
            continue

        file_violations = check_file(filepath, args.allow_legacy)
        violations.extend(file_violations)

    if violations:
        print("\n❌ Found os.path usage - please use pathlib.Path instead:\n")
        for filename, line, message in violations:
            print(f"  {filename}:{line}: {message}")

        print(
            "\n💡 Tip: pathlib.Path provides a more modern and cross-platform API."
        )
        print(
            "  Example: Path('dir') / 'file.txt' instead of os.path.join('dir', 'file.txt')"
        )
        print(
            "\n📚 See https://docs.python.org/3/library/pathlib.html for more information.\n"
        )
        return 1

    return 0


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