"""Context builder — extracts minimal, relevant code context for LLM agents.

The goal is to keep token usage low while giving agents enough information
to reason about a vulnerability and its surrounding code.
"""

from __future__ import annotations

import logging
from pathlib import Path

from secagent.utils.file_loader import read_file_lines

logger = logging.getLogger(__name__)

_DEFAULT_WINDOW = 150  # lines above/below the vulnerable line
_MAX_CONTEXT_LINES = 400


def build_context(
    file_path: str | Path,
    line: int,
    *,
    window: int = _DEFAULT_WINDOW,
) -> str:
    """Return a focused code window around *line* in *file_path*.

    The window is clamped to ``_MAX_CONTEXT_LINES`` to limit token usage.

    Parameters
    ----------
    file_path:
        Absolute or relative path to the source file.
    line:
        1-based line number of the vulnerability.
    window:
        Number of lines to include above and below *line*.

    Returns
    -------
    str
        Code snippet with line numbers prepended for clarity.
    """
    lines = read_file_lines(str(file_path))
    if not lines:
        return ""

    total = len(lines)
    start = max(0, line - 1 - window)
    end = min(total, line + window)

    # Clamp to max context
    if end - start > _MAX_CONTEXT_LINES:
        half = _MAX_CONTEXT_LINES // 2
        centre = line - 1
        start = max(0, centre - half)
        end = min(total, centre + half)

    snippet_lines: list[str] = []
    for idx in range(start, end):
        lineno = idx + 1
        marker = " >>> " if lineno == line else "     "
        snippet_lines.append(f"{lineno:>5}{marker}{lines[idx].rstrip()}")

    return "\n".join(snippet_lines)


def extract_snippet(
    file_path: str | Path,
    line: int,
    *,
    radius: int = 5,
) -> str:
    """Return a small snippet (± *radius* lines) for inline display.

    Unlike :func:`build_context`, this is intended for embedding in
    vulnerability descriptions, not for full LLM reasoning.
    """
    lines = read_file_lines(str(file_path))
    if not lines:
        return ""

    total = len(lines)
    start = max(0, line - 1 - radius)
    end = min(total, line + radius)

    return "".join(lines[start:end])
