"""End-to-end: command_id correlation across the captured log stream.

Verifies the recipe's promise that ``command_id`` lets a consumer
look up every line of a launched process's output via a single
log filter::

    $ jq 'select(.extra.command_id == "ab12cd34")' logs/myapp.log
    … prints every line the alembic process emitted, in order …

The test simulates the consumer's grep / jq filter against the
caplog records, asserting:

  - Every captured stdout/stderr line carries the right ``command_id``
    in ``extra``.
  - The IDs are unique across two concurrent ``run()`` invocations
    so cross-process correlation isn't ambiguous.
  - For ``Service`` lifecycles, both the streamer records AND the
    Service's own "service starting" / "service ready" / "service
    stopped" records share the same ``command_id``.
"""

from __future__ import annotations

import logging
import socket
import sys
import textwrap
import threading

import pytest

pytestmark = pytest.mark.e2e


def _free_port() -> int:
    s = socket.socket()
    s.bind(("127.0.0.1", 0))
    port = s.getsockname()[1]
    s.close()
    return port


@pytest.fixture
def bootstrapped_test_app(tmp_path, monkeypatch):
    monkeypatch.setenv("XDG_STATE_HOME", str(tmp_path))
    monkeypatch.setenv("HOME", str(tmp_path))
    from mgf.common import bootstrap_for_test

    ctx = bootstrap_for_test()
    try:
        yield ctx
    finally:
        ctx.shutdown()


def test_command_id_on_every_streamed_record(
    bootstrapped_test_app,
    caplog,
) -> None:
    """The consumer's `jq | grep command_id=…` works."""
    from mgf.common.process import run

    with caplog.at_level(logging.INFO, logger="mgf.process"):
        result = run(
            [
                sys.executable,
                "-c",
                "print('line-1'); print('line-2'); import sys; sys.stderr.write('warn-1\\n')",
            ],
        )

    cmd_id = result.command_id
    # Find every record this run produced.
    records = [
        r
        for r in caplog.records
        if r.name.startswith("mgf.process.") and getattr(r, "command_id", None) == cmd_id
    ]
    # Should have 3 — line-1, line-2, warn-1.
    assert len(records) == 3, (
        f"Expected 3 records for command_id={cmd_id}; got {[r.message for r in records]}"
    )
    # Verify the streams partition correctly.
    streams = {r.message: r.stream for r in records}
    assert streams["line-1"] == "stdout"
    assert streams["line-2"] == "stdout"
    assert streams["warn-1"] == "stderr"
    # And the levels.
    levels = {r.message: r.levelname for r in records}
    assert levels["line-1"] == "INFO"
    assert levels["warn-1"] == "WARNING"


def test_concurrent_runs_have_distinct_command_ids(
    bootstrapped_test_app,
    caplog,
) -> None:
    """Two threads each call run(); each gets a unique command_id."""
    from mgf.common.process import run

    results: list[object] = [None, None]

    def _runner(idx: int, payload: str) -> None:
        results[idx] = run(
            [sys.executable, "-c", f"print({payload!r})"],
        )

    with caplog.at_level(logging.INFO, logger="mgf.process"):
        t1 = threading.Thread(target=_runner, args=(0, "alpha"))
        t2 = threading.Thread(target=_runner, args=(1, "beta"))
        t1.start()
        t2.start()
        t1.join()
        t2.join()

    assert results[0] is not None and results[1] is not None
    cmd_ids = {results[0].command_id, results[1].command_id}
    assert len(cmd_ids) == 2, f"Expected distinct command_ids; got {cmd_ids}"

    # Each result's command_id appears on exactly one record.
    by_id: dict[str, list[str]] = {cid: [] for cid in cmd_ids}
    for r in caplog.records:
        cid = getattr(r, "command_id", None)
        if cid in by_id:
            by_id[cid].append(r.message)
    assert by_id[results[0].command_id] == ["alpha"]
    assert by_id[results[1].command_id] == ["beta"]


_TINY_LISTENER = textwrap.dedent(
    """
    import socket, sys
    sock = socket.socket(); sock.bind(("127.0.0.1", PORT)); sock.listen(1)
    print("READY", flush=True)
    while True:
        try:
            conn, _ = sock.accept(); conn.close()
        except KeyboardInterrupt:
            break
    """,
).strip()


def test_service_records_share_command_id(
    bootstrapped_test_app,
    caplog,
) -> None:
    """A Service emits records on two loggers (mgf.service.<name> for
    its own lifecycle + mgf.process.<name> for child output). All
    share the same command_id."""
    from mgf.common.process import LogLineReady, Service

    port = _free_port()
    with (
        caplog.at_level(logging.INFO, logger="mgf"),
        Service(
            [sys.executable, "-c", _TINY_LISTENER.replace("PORT", str(port))],
            name="corr-svc",
            ready=LogLineReady(r"^READY$", stream="stdout"),
            timeout_s=5.0,
            poll_interval_s=0.05,
            kill_grace_s=1.0,
        ) as svc,
    ):
        assert svc.is_running

    cmd_id = svc.command_id
    # Records on both logger trees with this command_id.
    by_logger: dict[str, list[str]] = {}
    for r in caplog.records:
        if getattr(r, "command_id", None) != cmd_id:
            continue
        by_logger.setdefault(r.name, []).append(r.message)

    # Service lifecycle logger — at minimum "service starting" and
    # "service stopped" (ready may or may not appear in caplog
    # depending on level routing).
    service_msgs = by_logger.get("mgf.service.corr-svc", [])
    assert "service starting" in service_msgs
    assert "service stopped" in service_msgs

    # Process stream logger — at least the READY line.
    process_msgs = by_logger.get("mgf.process.corr-svc", [])
    assert any("READY" in m for m in process_msgs)
