"""Passive LSP diagnostics — quality gate for file writes/edits.

Mirrors the ``auto_lint()`` contract in ``otto/lint.py``: returns a formatted
error string or ``None``, never raises.
"""

from pathlib import Path

from otto.lsp.pool import LSPPool

_MAX_DIAGNOSTICS = 20
_MAX_OUTPUT_CHARS = 2000


async def auto_diagnostics(pool: LSPPool | None, path: Path) -> str | None:
    """Run LSP diagnostics on a file, returning formatted errors or None.

    - Returns None if pool is None, no server available, or no errors.
    - Filters to severity 1 (errors only), max 20 per file.
    - Truncates output at 2000 chars.
    - Catches all exceptions — never breaks tool execution.
    """
    if pool is None:
        return None

    try:
        resolved = path.resolve()

        # Read current file content and notify the LSP
        try:
            content = resolved.read_text(encoding="utf-8", errors="replace")
        except OSError:
            return None

        await pool.touch_file(resolved, content)
        diagnostics = await pool.get_diagnostics(resolved)

        if not diagnostics:
            return None

        # Filter to errors only (severity 1)
        errors = [d for d in diagnostics if d.severity == 1]
        if not errors:
            return None

        # Format
        lines: list[str] = []
        for d in errors[:_MAX_DIAGNOSTICS]:
            source_tag = f" [{d.source}]" if d.source else ""
            lines.append(f"{d.path}:{d.line}:{d.character}: {d.message}{source_tag}")

        if len(errors) > _MAX_DIAGNOSTICS:
            lines.append(f"... and {len(errors) - _MAX_DIAGNOSTICS} more errors")

        output = "\n".join(lines)
        if len(output) > _MAX_OUTPUT_CHARS:
            output = output[:_MAX_OUTPUT_CHARS] + "\n... (truncated)"

        return output

    except Exception:
        return None
