"""Tests for roam x-lang -- cross-language bridge detection command."""

from __future__ import annotations

import pytest

from tests.conftest import (
    assert_json_envelope,
    git_init,
    index_in_process,
    invoke_cli,
    parse_json_output,
)

# ---------------------------------------------------------------------------
# Fixtures
# ---------------------------------------------------------------------------


@pytest.fixture
def python_only_project(tmp_path):
    """A Python-only project with multiple files.

    x-lang should detect no bridges here (no .proto, no Apex, etc.)
    and exit cleanly with the 'no cross-language bridges detected' verdict.
    """
    proj = tmp_path / "xlang_py"
    proj.mkdir()
    (proj / ".gitignore").write_text(".roam/\n")

    src = proj / "src"
    src.mkdir()
    (src / "models.py").write_text(
        "class User:\n"
        '    """A user model."""\n'
        "    def __init__(self, name):\n"
        "        self.name = name\n"
        "\n\n"
        "class Product:\n"
        '    """A product."""\n'
        "    def __init__(self, sku, price):\n"
        "        self.sku = sku\n"
        "        self.price = price\n"
    )
    (src / "service.py").write_text(
        "from models import User, Product\n"
        "\n\n"
        "def get_user(uid):\n"
        '    """Fetch a user by id."""\n'
        "    return User(uid)\n"
        "\n\n"
        "def list_products():\n"
        '    """Return all products."""\n'
        "    return []\n"
    )
    (src / "utils.py").write_text(
        "def slugify(text):\n"
        '    """Convert text to a slug."""\n'
        "    return text.lower().replace(' ', '-')\n"
        "\n\n"
        "def clamp(value, lo, hi):\n"
        '    """Clamp value between lo and hi."""\n'
        "    return max(lo, min(hi, value))\n"
    )

    git_init(proj)
    index_in_process(proj)
    return proj


@pytest.fixture
def proto_python_project(tmp_path):
    """A project that has a .proto file alongside a Python stub.

    This triggers the ProtobufBridge and exercises the bridge-detection
    and link-resolution code paths.
    """
    proj = tmp_path / "xlang_proto"
    proj.mkdir()
    (proj / ".gitignore").write_text(".roam/\n")

    proto_dir = proj / "proto"
    proto_dir.mkdir()
    (proto_dir / "user.proto").write_text(
        'syntax = "proto3";\n'
        "package user;\n\n"
        "message UserRequest {\n"
        "  string user_id = 1;\n"
        "}\n\n"
        "message UserResponse {\n"
        "  string name = 1;\n"
        "  string email = 2;\n"
        "}\n\n"
        "service UserService {\n"
        "  rpc GetUser (UserRequest) returns (UserResponse);\n"
        "}\n"
    )

    gen_dir = proj / "gen"
    gen_dir.mkdir()
    (gen_dir / "user_pb2.py").write_text(
        "# Generated by protoc\n"
        "class UserRequest:\n"
        "    user_id: str = ''\n"
        "\n\n"
        "class UserResponse:\n"
        "    name: str = ''\n"
        "    email: str = ''\n"
    )

    (proj / "app.py").write_text(
        "from gen.user_pb2 import UserRequest, UserResponse\n"
        "\n\n"
        "def handle(req):\n"
        '    """Handle a gRPC request."""\n'
        "    return UserResponse(name='Alice', email='alice@example.com')\n"
    )

    git_init(proj)
    index_in_process(proj)
    return proj


# ---------------------------------------------------------------------------
# Smoke tests
# ---------------------------------------------------------------------------


class TestXLangSmoke:
    def test_exits_zero_no_bridges(self, cli_runner, python_only_project, monkeypatch):
        """x-lang exits 0 even when no cross-language bridges are detected."""
        monkeypatch.chdir(python_only_project)
        result = invoke_cli(cli_runner, ["x-lang"], cwd=python_only_project)
        assert result.exit_code == 0, f"x-lang exited non-zero:\n{result.output}"

    def test_exits_zero_with_proto_project(self, cli_runner, proto_python_project, monkeypatch):
        """x-lang exits 0 on a project with .proto files."""
        monkeypatch.chdir(proto_python_project)
        result = invoke_cli(cli_runner, ["x-lang"], cwd=proto_python_project)
        assert result.exit_code == 0, f"x-lang exited non-zero:\n{result.output}"


# ---------------------------------------------------------------------------
# JSON output tests
# ---------------------------------------------------------------------------


class TestXLangJSON:
    def test_json_envelope_no_bridges(self, cli_runner, python_only_project, monkeypatch):
        monkeypatch.chdir(python_only_project)
        result = invoke_cli(cli_runner, ["x-lang"], cwd=python_only_project, json_mode=True)
        data = parse_json_output(result, "x-lang")
        assert_json_envelope(data, "x-lang")

    def test_json_summary_has_verdict(self, cli_runner, python_only_project, monkeypatch):
        monkeypatch.chdir(python_only_project)
        result = invoke_cli(cli_runner, ["x-lang"], cwd=python_only_project, json_mode=True)
        data = parse_json_output(result, "x-lang")
        summary = data["summary"]
        assert "verdict" in summary, f"summary missing 'verdict': {summary}"
        assert isinstance(summary["verdict"], str)
        assert summary["verdict"]

    def test_json_summary_has_bridges_count(self, cli_runner, python_only_project, monkeypatch):
        monkeypatch.chdir(python_only_project)
        result = invoke_cli(cli_runner, ["x-lang"], cwd=python_only_project, json_mode=True)
        data = parse_json_output(result, "x-lang")
        summary = data["summary"]
        assert "bridges" in summary, f"summary missing 'bridges': {summary}"
        assert isinstance(summary["bridges"], int)

    def test_json_summary_has_links_count(self, cli_runner, python_only_project, monkeypatch):
        monkeypatch.chdir(python_only_project)
        result = invoke_cli(cli_runner, ["x-lang"], cwd=python_only_project, json_mode=True)
        data = parse_json_output(result, "x-lang")
        summary = data["summary"]
        assert "links" in summary, f"summary missing 'links': {summary}"
        assert isinstance(summary["links"], int)

    def test_json_has_bridges_list(self, cli_runner, python_only_project, monkeypatch):
        monkeypatch.chdir(python_only_project)
        result = invoke_cli(cli_runner, ["x-lang"], cwd=python_only_project, json_mode=True)
        data = parse_json_output(result, "x-lang")
        assert "bridges" in data, f"Top-level 'bridges' key missing from envelope: {list(data)}"
        assert isinstance(data["bridges"], list)

    def test_json_command_field_is_x_lang(self, cli_runner, python_only_project, monkeypatch):
        monkeypatch.chdir(python_only_project)
        result = invoke_cli(cli_runner, ["x-lang"], cwd=python_only_project, json_mode=True)
        data = parse_json_output(result, "x-lang")
        assert data["command"] == "x-lang"

    def test_json_no_bridges_verdict_text(self, cli_runner, python_only_project, monkeypatch):
        """Python-only project should report zero bridges detected."""
        monkeypatch.chdir(python_only_project)
        result = invoke_cli(cli_runner, ["x-lang"], cwd=python_only_project, json_mode=True)
        data = parse_json_output(result, "x-lang")
        assert data["summary"]["bridges"] == 0

    def test_json_proto_project_envelope(self, cli_runner, proto_python_project, monkeypatch):
        monkeypatch.chdir(proto_python_project)
        result = invoke_cli(cli_runner, ["x-lang"], cwd=proto_python_project, json_mode=True)
        data = parse_json_output(result, "x-lang")
        assert_json_envelope(data, "x-lang")


# ---------------------------------------------------------------------------
# Text output tests
# ---------------------------------------------------------------------------


class TestXLangText:
    def test_verdict_line_present_no_bridges(self, cli_runner, python_only_project, monkeypatch):
        monkeypatch.chdir(python_only_project)
        result = invoke_cli(cli_runner, ["x-lang"], cwd=python_only_project)
        assert "VERDICT:" in result.output, f"Expected 'VERDICT:' in output, got:\n{result.output}"

    def test_output_mentions_bridges(self, cli_runner, python_only_project, monkeypatch):
        """Text output should reference 'bridges' in some form."""
        monkeypatch.chdir(python_only_project)
        result = invoke_cli(cli_runner, ["x-lang"], cwd=python_only_project)
        lower = result.output.lower()
        assert "bridge" in lower or "cross-language" in lower or "no files" in lower, (
            f"Expected 'bridge' or 'cross-language' in output, got:\n{result.output}"
        )

    def test_verdict_line_present_proto_project(self, cli_runner, proto_python_project, monkeypatch):
        monkeypatch.chdir(proto_python_project)
        result = invoke_cli(cli_runner, ["x-lang"], cwd=proto_python_project)
        assert "VERDICT:" in result.output, f"Expected 'VERDICT:' in output, got:\n{result.output}"
