import os
import ast
from typing import Dict, List, Any

try:
    import toml
except ImportError:
    toml = None

# Static directories to skip
EXCLUDE_DIRS = {
    "__pycache__", "tests", ".git", ".vscode", ".idea",
    "node_modules", "dist", "build", ".next", "vendor"
}

# File extensions and names we consider "important"
INCLUDE_EXTS = {
    # Backend and scripts
    ".py", ".sh", ".rb", ".go",
    # Configuration
    ".json", ".yml", ".yaml", "Dockerfile", "docker-compose.yml",
    # Front-end
    ".html", ".css", ".js", ".jsx", ".ts", ".tsx",
    # Markdown and docs
    ".md", ".rst"
}

# Patterns or exact filenames to skip
EXCLUDE_FILES = {
    ".env", "requirements.txt", "Pipfile.lock",
    "package-lock.json", "yarn.lock", "composer.json"
}

def load_pyproject_info(start_path: str) -> Dict[str, Any]:
    toml_path = os.path.join(start_path, "pyproject.toml")
    if toml and os.path.isfile(toml_path):
        data = toml.load(toml_path).get("tool", {}).get("poetry", {}) \
            or toml.load(toml_path).get("project", {})
        return {
            "name": data.get("name"),
            "version": data.get("version"),
            "description": data.get("description"),
            "authors": data.get("authors", []),
            "dependencies": list(data.get("dependencies", {}).keys())
        }
    return {}

def is_virtual_env_folder(path: str) -> bool:
    """
    Detects if a directory is a Python virtual environment by common indicators.
    """
    if os.path.isfile(os.path.join(path, "pyvenv.cfg")):
        return True
    if os.path.isdir(os.path.join(path, "bin")) and \
       os.path.isfile(os.path.join(path, "bin", "activate")):
        return True
    if os.path.isdir(os.path.join(path, "Scripts")) and \
       os.path.isfile(os.path.join(path, "Scripts", "activate.bat")):
        return True
    return False

def find_important_files(start_path: str) -> List[str]:
    files_list: List[str] = []
    for root, dirs, files in os.walk(start_path):
        kept_dirs = []
        for d in dirs:
            full = os.path.join(root, d)
            if d in EXCLUDE_DIRS:
                continue
            if is_virtual_env_folder(full):
                continue
            kept_dirs.append(d)
        dirs[:] = kept_dirs

        for fname in files:
            fpath = os.path.join(root, fname)
            if any(fname.startswith(p.rstrip('*')) for p in EXCLUDE_FILES):
                continue
            name, ext = os.path.splitext(fname)
            if ext in INCLUDE_EXTS or fname in INCLUDE_EXTS:
                files_list.append(fpath)
    return files_list

def parse_python_info(path: str) -> Dict[str, Any]:
    with open(path, "r", encoding="utf-8") as f:
        source = f.read()
    tree = ast.parse(source, filename=path)
    module_doc = ast.get_docstring(tree)
    classes, functions = [], []
    for node in tree.body:
        if isinstance(node, ast.ClassDef):
            classes.append({
                "name": node.name,
                "doc": ast.get_docstring(node)
            })
        elif isinstance(node, ast.FunctionDef):
            functions.append({
                "name": node.name,
                "doc": ast.get_docstring(node),
                "args": [arg.arg for arg in node.args.args]
            })
    return {"classes": classes, "functions": functions, "module_doc": module_doc}

def get_file_summary(path: str) -> Dict[str, Any]:
    summary: Dict[str, Any] = {"path": path}
    try:
        summary["size_bytes"] = os.path.getsize(path)
    except OSError:
        summary["size_bytes"] = None

    ext = os.path.splitext(path)[1]
    if ext == ".py":
        summary.update(parse_python_info(path))
    else:
        try:
            with open(path, "r", encoding="utf-8", errors="ignore") as f:
                preview = [next(f).rstrip() for _ in range(5)]
            summary["preview"] = preview
        except Exception:
            summary["preview"] = []
    return summary

def get_project_review(start_path: str = ".") -> Dict[str, Any]:
    """
    Gathers project metadata and summaries of all important files, including root directory name.
    """
    review: Dict[str, Any] = {}
    # Root project name
    review["root_directory"] = os.path.basename(os.path.abspath(start_path))
    # Project metadata
    review["metadata"] = load_pyproject_info(start_path)
    # File summaries
    files = find_important_files(start_path)
    review["files"] = [get_file_summary(f) for f in files]
    return review
