"""Regression tests for port configuration and tool registration.

These tests prevent the recurrence of two bugs found on 2026-07-01:

1. **Port conflict (login hijack)**: vite.config.ts hardcoded proxy target
   to ``localhost:8000``, which was occupied by the ``geo_backend`` Docker
   container.  Login requests were silently proxied to the wrong service,
   returning a "请求参数校验失败" error because geo_backend expected an
   ``email`` field while the frontend sent ``username``.

2. **Missing tool registration**: ``benchmark_runner.yaml`` referenced
   ``file_read`` but the tool's actual name is ``read_file``, and
   ``ReadFileTool`` was never registered in ``app.py``'s lifespan — causing
   "Tool not found: file_read" at skill-load time.

3. **.env.dev not loaded**: ``load_dotenv`` only looked for ``.env``,
   missing ``.env.dev`` (the in-tree dev preset with DATABASE_URL /
   REDIS_URL / port allocation), causing Bitable init failure.

The tests are intentionally static (grep-based) — they verify that the
canonical port numbers and tool names are consistent across all config
layers, without booting a server.
"""

from __future__ import annotations

import re
from pathlib import Path

import pytest

# Project root — tests/unit/server/ → ../../../..
PROJECT_ROOT = Path(__file__).resolve().parents[3]


# ---------------------------------------------------------------------------
# 1. Port configuration — no legacy 8000/8001/5173 in canonical files
# ---------------------------------------------------------------------------

# Files where hardcoded 8000 / 8001 / 5173 are bugs (should use 18001 etc.)
_FILES_NO_LEGACY_PORTS = [
    "agentkit.yaml",
    "src/agentkit/server/config.py",
    "src/agentkit/server/client.py",
    "src/agentkit/cli/main.py",
    "src/agentkit/cli/templates.py",
    "src/agentkit/cli/onboarding.py",
    "src/agentkit/cli/admin_client.py",
    "src/agentkit/cli/admin.py",
    "src/agentkit/cli/pair.py",
    "src/agentkit/cli/init.py",
    "src/agentkit/client/sync.py",
    "src/agentkit/tools/bitable_tool.py",
    "src/agentkit/memory/http_rag.py",
    "src/agentkit/memory/adapters/generic_http.py",
    "src/agentkit/server/frontend/vite.config.ts",
    "src/agentkit/server/frontend/src-tauri/src/lib.rs",
    "src/agentkit/server/frontend/src-tauri/tauri.conf.json",
    "src/agentkit/server/frontend/src/stores/settings.ts",
    "src/agentkit/server/frontend/src/stores/auth.ts",
    "src/agentkit/server/frontend/playwright.config.ts",
]

# Patterns that indicate a legacy hardcoded port (not inside a comment-only line).
# Matches :8000, :8001, :5173, :8002, port=8000, port=8001, port: 8001 etc.
_LEGACY_PORT_RE = re.compile(
    r"""(?:                   # Match any of:
        :8000                 # URL-style :8000
        | :8001               # URL-style :8001
        | :8002               # URL-style :8002
        | :5173               # Vite default :5173
        | port['"]?\s*[:=]\s*800[012]\b  # port: 8001 / port = 8001 / port' = 8001
        | port['"]?\s*[:=]\s*5173\b      # port: 5173
    )""",
    re.VERBOSE,
)


@pytest.mark.parametrize("rel_path", _FILES_NO_LEGACY_PORTS)
def test_no_legacy_hardcoded_ports(rel_path: str) -> None:
    """Canonical config/source files must not hardcode 8000/8001/5173/8002.

    These ports conflict with Docker services (geo_backend:8000) or other
    dev projects (Vite:5173).  The canonical ports are 18001 (backend),
    18002 (GUI), 15173 (Vite dev), 15174 (HMR).

    Exceptions: comments mentioning the old ports for historical context
    are allowed — we only flag actual code/config lines.
    """
    fpath = PROJECT_ROOT / rel_path
    if not fpath.exists():
        pytest.skip(f"File not found: {rel_path}")

    content = fpath.read_text(encoding="utf-8")
    offending = []
    for lineno, line in enumerate(content.splitlines(), 1):
        stripped = line.strip()
        # Skip comment-only lines (Python #, YAML #, Rust //, TS //)
        if stripped.startswith(("#", "//")) or stripped.startswith("///"):
            continue
        if _LEGACY_PORT_RE.search(line):
            offending.append(f"  {rel_path}:{lineno}: {stripped}")
    assert not offending, (
        f"Legacy port (8000/8001/5173/8002) found in {rel_path}:\n"
        + "\n".join(offending)
        + "\nUse 18001 (backend) / 18002 (GUI) / 15173 (Vite) / 15174 (HMR)."
    )


# ---------------------------------------------------------------------------
# 2. Tool registration — ReadFileTool registered + yaml name matches
# ---------------------------------------------------------------------------


def test_readfiletool_registered_in_app_py() -> None:
    """app.py lifespan must register ReadFileTool so benchmark_runner works."""
    app_py = (PROJECT_ROOT / "src/agentkit/server/app.py").read_text(encoding="utf-8")
    assert "from agentkit.tools.file_read import ReadFileTool" in app_py, (
        "ReadFileTool import missing from app.py — benchmark_runner skill "
        "will fail with 'Tool not found: read_file'"
    )
    assert "register(ReadFileTool()" in app_py, (
        "ReadFileTool registration call missing from app.py lifespan"
    )


def test_benchmark_runner_yaml_uses_correct_tool_name() -> None:
    """benchmark_runner.yaml must reference 'read_file' (not 'file_read')."""
    yaml_path = PROJECT_ROOT / "configs/skills/benchmark_runner.yaml"
    if not yaml_path.exists():
        pytest.skip("benchmark_runner.yaml not found")
    content = yaml_path.read_text(encoding="utf-8")
    # The tools section should contain read_file, not file_read / file_write
    assert "file_read" not in content or "read_file" in content, (
        "benchmark_runner.yaml references 'file_read' — the tool's actual "
        "name is 'read_file'. This causes 'Tool not found' at load time."
    )


# ---------------------------------------------------------------------------
# 3. .env.dev loading — load_dotenv supports .env.dev candidate
# ---------------------------------------------------------------------------


def test_load_dotenv_supports_env_dev() -> None:
    """app.py must load .env.dev (not just .env) for dev port/DB config."""
    app_py = (PROJECT_ROOT / "src/agentkit/server/app.py").read_text(encoding="utf-8")
    assert ".env.dev" in app_py, (
        "app.py does not load .env.dev — DATABASE_URL / REDIS_URL / port "
        "overrides in .env.dev will be silently ignored, causing Bitable "
        "init failure and Redis 'not_configured' status."
    )


def test_env_dev_contains_required_keys() -> None:
    """ .env.dev must define all keys needed for full functional testing."""
    env_dev = PROJECT_ROOT / ".env.dev"
    if not env_dev.exists():
        pytest.skip(".env.dev not found")
    content = env_dev.read_text(encoding="utf-8")
    required_keys = [
        "AGENTKIT_SERVER_PORT",
        "BACKEND_PORT",
        "VITE_DEV_PORT",
        "VITE_HMR_PORT",
        "AGENTKIT_REMOTE_PORT",
        "DATABASE_URL",
        "REDIS_URL",
        "AGENTKIT_SESSION_BACKEND",
        "AGENTKIT_BUS_BACKEND",
        "AGENTKIT_TASK_STORE_BACKEND",
    ]
    missing = [k for k in required_keys if k not in content]
    assert not missing, f".env.dev missing required keys: {missing}"
