"""Code structure extraction for the project skeleton map.

Extracted from ``scanner.py`` to reduce file size.  Produces the
Markdown content written to ``.antigravity/structure.md``.

This module generates a **language-agnostic** file tree with line counts.
Code analysis is performed entirely by LLMs reading full source code
(see the agent.md refresh pipeline), not by AST or regex parsing.
"""
from __future__ import annotations

from pathlib import Path

from antigravity_engine.hub._constants import LANG_MAP


# ---------------------------------------------------------------------------
# Limits
# ---------------------------------------------------------------------------

# Max total size of the generated structure text.
_STRUCTURE_LIMIT = 50_000


# ---------------------------------------------------------------------------
# Public API
# ---------------------------------------------------------------------------

def extract_structure(root: Path) -> str:
    """Generate a file tree map of the project.

    Language-agnostic: lists source files with line counts, organized by
    directory.  No AST parsing, no regex — just a structural overview.
    Detailed code analysis is handled by LLM agents (see ``agents/*.md``).

    The output is a Markdown-formatted string organized by directory,
    suitable for writing to ``.antigravity/structure.md``.

    Args:
        root: Project root directory.

    Returns:
        Markdown string describing the project file structure.
    """
    from antigravity_engine.hub.scanner import _find_venv_dirs, _should_skip

    venv_dirs = _find_venv_dirs(root)
    sections: list[str] = []
    total_chars = 0

    # Collect all source files grouped by directory.
    dir_files: dict[str, list[Path]] = {}
    for item in sorted(root.rglob("*")):
        if not item.is_file():
            continue
        try:
            rel = item.relative_to(root)
        except ValueError:
            continue
        if _should_skip(rel, venv_dirs):
            continue
        ext = item.suffix.lower()
        if ext not in LANG_MAP:
            continue
        parent = str(rel.parent) if str(rel.parent) != "." else "."
        dir_files.setdefault(parent, []).append(item)

    for dir_path in sorted(dir_files):
        dir_lines: list[str] = []
        display_dir = dir_path if dir_path != "." else "(root)"
        file_count = len(dir_files[dir_path])
        total_lines = 0

        for fpath in sorted(dir_files[dir_path]):
            rel = fpath.relative_to(root)
            lang = LANG_MAP.get(fpath.suffix.lower(), "")
            try:
                lc = len(fpath.read_text(encoding="utf-8", errors="replace").splitlines())
            except OSError:
                lc = 0
            total_lines += lc
            lang_tag = f" [{lang}]" if lang else ""
            dir_lines.append(f"- `{rel}`{lang_tag} ({lc} lines)")

        header = f"## {display_dir}/  ({file_count} files, {total_lines} lines)"
        section = header + "\n" + "\n".join(dir_lines)

        if total_chars + len(section) > _STRUCTURE_LIMIT:
            sections.append(f"\n(truncated — {_STRUCTURE_LIMIT} char limit reached)")
            break
        sections.append(section)
        total_chars += len(section)

    header = "# Project Structure Map\n\nAuto-generated by `ag refresh`. Do not edit manually.\n"
    return header + "\n\n".join(sections)


def generate_module_context(root: Path, module_name: str) -> str:
    """Generate a file tree for a single module.

    Language-agnostic: lists source files with line counts.
    Detailed code analysis is handled by LLM agents.

    Args:
        root: Project root directory.
        module_name: Name of the top-level module directory.

    Returns:
        Markdown string with the module's file tree.
    """
    from antigravity_engine.hub.scanner import _find_venv_dirs, _should_skip

    module_path = root / module_name
    if not module_path.is_dir():
        return f"# {module_name}\n\nModule directory not found."

    venv_dirs = _find_venv_dirs(root)
    sections: list[str] = [f"# Module: {module_name}\n"]

    dir_files: dict[str, list[Path]] = {}
    for item in sorted(module_path.rglob("*")):
        if not item.is_file():
            continue
        try:
            rel = item.relative_to(root)
        except ValueError:
            continue
        if _should_skip(rel, venv_dirs):
            continue
        ext = item.suffix.lower()
        if ext not in LANG_MAP:
            continue
        parent = str(rel.parent) if str(rel.parent) != "." else module_name
        dir_files.setdefault(parent, []).append(item)

    for dir_path in sorted(dir_files):
        dir_lines: list[str] = [f"## {dir_path}/"]
        for fpath in sorted(dir_files[dir_path]):
            rel = fpath.relative_to(root)
            lang = LANG_MAP.get(fpath.suffix.lower(), "")
            try:
                lc = len(fpath.read_text(encoding="utf-8", errors="replace").splitlines())
            except OSError:
                lc = 0
            lang_tag = f" [{lang}]" if lang else ""
            dir_lines.append(f"- `{rel}`{lang_tag} ({lc} lines)")

        sections.append("\n".join(dir_lines))

    return "\n\n".join(sections)
