"""Shared helper functions for LSP transport BDD step definitions.

Extracted common mock-building utilities so individual step files stay
under the 500-line limit (see CONTRIBUTING.md file-size guideline).
"""

from __future__ import annotations

import json
import subprocess
from typing import Any
from unittest.mock import MagicMock


def make_mock_process(
    *,
    poll_return: int | None = None,
    returncode: int = 0,
    stdin: object | None = "auto",
    stdout: object | None = "auto",
    stderr: object | None = "auto",
    pid: int = 12345,
) -> MagicMock:
    """Create a ``MagicMock`` resembling ``subprocess.Popen``.

    Args:
        poll_return: Value for :attr:`~MagicMock.poll` return value.
            ``None`` means the process is still running.
        returncode: The subprocess exit code when ``poll()`` returns.
        stdin: Stdin mock object (or ``"auto"`` for auto-generated).
        stdout: Stdout mock object (or ``"auto"`` for auto-generated).
        stderr: Stderr mock object (or ``"auto"`` for auto-generated).
        pid: Process ID to report.

    Returns:
        A fully configured ``MagicMock`` with ``subprocess.Popen`` spec.
    """
    proc = MagicMock(spec=subprocess.Popen)
    proc.pid = pid
    proc.poll.return_value = poll_return
    proc.returncode = returncode
    if stdin == "auto":
        proc.stdin = MagicMock()
    else:
        proc.stdin = stdin
    if stdout == "auto":
        proc.stdout = MagicMock()
    else:
        proc.stdout = stdout
    if stderr == "auto":
        proc.stderr = MagicMock()
    else:
        proc.stderr = stderr
    return proc


def build_lsp_frame(body_dict: dict[str, Any]) -> bytes:
    """Encode a dict as a Content-Length framed LSP message.

    Args:
        body_dict: The JSON-serialisable message body.

    Returns:
        Raw bytes including the ``Content-Length`` header and empty line prefix.
    """
    payload = json.dumps(body_dict, separators=(",", ":")).encode("utf-8")
    header = f"Content-Length: {len(payload)}\r\n\r\n".encode("ascii")
    return header + payload
