# SPDX-License-Identifier: AGPL-3.0-only
# Copyright (c) 2026 sol pbc

from __future__ import annotations

import re
import subprocess
from pathlib import Path

from solstone.think.sol_cli import ALIASES, COMMANDS

EXCLUDED_DIRS = {
    ".git",
    ".venv",
    "build",
    "dist",
    "htmlcov",
    "journal",
    "logs",
    "node_modules",
    "scratch",
    "tmp",
    "vpe",
}
TEXT_SUFFIXES = {
    ".html",
    ".js",
    ".md",
    ".py",
    ".rules",
    ".sh",
    ".toml",
    ".txt",
    ".yml",
    ".yaml",
}
ROOT_TEXT_FILES = {
    "AGENTS.md",
    "CONTRIBUTING.md",
    "INSTALL.md",
    "Makefile",
    "README.md",
}
ACCESS_POSITIVE_EXPECTATIONS = {
    Path("AGENTS.md"): re.compile(r"\bsol call\b"),
    Path("INSTALL.md"): re.compile(r"\bsol skills\b"),
    Path("Makefile"): re.compile(r"\$\(VENV_BIN\)/sol skills\b"),
    Path("README.md"): re.compile(r"\bsol chat\b"),
}


def _is_text_surface(path: Path) -> bool:
    if any(part in EXCLUDED_DIRS for part in path.parts):
        return False
    if path.name == "CHANGELOG.md":
        return False
    if path.name in ROOT_TEXT_FILES:
        return True
    return path.suffix in TEXT_SUFFIXES


def _tracked_sol_lines() -> list[tuple[Path, int, str]]:
    result = subprocess.run(
        ["git", "grep", "-n", "-I", "-F", "sol ", "--"],
        check=False,
        capture_output=True,
        text=True,
    )
    if result.returncode not in {0, 1}:
        result.check_returncode()
    matches = []
    for raw_line in result.stdout.splitlines():
        path_text, line_number, line = raw_line.split(":", maxsplit=2)
        path = Path(path_text)
        if path.exists() and _is_text_surface(path):
            matches.append((path, int(line_number), line))
    return matches


def _skip_line(path: Path, line: str) -> bool:
    if path in {
        Path("tests/test_cli_prog_fidelity.py"),
        Path("tests/test_journal_cli_migration.py"),
    }:
        return True
    if (
        path == Path("tests/test_install_guard.py")
        and "managed by 'sol config'" in line
    ):
        return True
    # "how should sol think?" is owner-facing init wizard copy (a question to the
    # owner), not a `sol think` service-command reference. Exempt the prose phrase
    # wherever it appears (the init template and the tests that assert it).
    if "how should sol think" in line:
        return True
    # "let sol describe what's in it" is owner-facing import-affordance copy (sol
    # the keeper describing an image), not a `sol describe` service-command
    # reference. Exempt the prose phrase wherever it appears.
    if "let sol describe what's in it" in line:
        return True
    if path.parts and path.parts[0] == "tests":
        argv_markers = (
            "sys.argv",
            '["sol ',
            "usage: sol ",
            "run_main(mod,",
            "sol think.talents",
        )
        if any(marker in line for marker in argv_markers):
            return True
        if re.search(
            r'"sol (' + "|".join(map(re.escape, SERVICE_TERMS)) + r")\b", line
        ):
            return True
    return False


SERVICE_TERMS = sorted(
    {
        *(name for name, command in COMMANDS.items() if command.surface == "service"),
        *(name for name, alias in ALIASES.items() if alias.surface == "service"),
    },
    key=len,
    reverse=True,
)
SERVICE_SOL_RE = re.compile(
    r"\bsol (" + "|".join(map(re.escape, SERVICE_TERMS)) + r")\b"
)
SERVICE_SOL_LITERAL_RE = re.compile(
    r"""['"]sol['"]\s*,\s*['"](?:"""
    + "|".join(map(re.escape, SERVICE_TERMS))
    + r""")['"]"""
)


def test_service_tagged_commands_are_not_documented_as_sol() -> None:
    service_matches = [
        f"{path}:{line_number}: {line}"
        for path, line_number, line in _tracked_sol_lines()
        if not _skip_line(path, line) and SERVICE_SOL_RE.search(line)
    ]
    missing_access_expectations = [
        str(path)
        for path, expectation in ACCESS_POSITIVE_EXPECTATIONS.items()
        if not path.exists()
        or expectation.search(path.read_text(encoding="utf-8")) is None
    ]

    assert service_matches == []
    assert missing_access_expectations == []


def _production_python_files() -> list[Path]:
    return sorted(
        path
        for path in Path("solstone").rglob("*.py")
        if path.is_file() and "tests" not in path.parts
    )


def test_production_service_commands_do_not_dispatch_through_sol() -> None:
    matches = []
    for path in _production_python_files():
        text = path.read_text(encoding="utf-8")
        for match in SERVICE_SOL_LITERAL_RE.finditer(text):
            line_number = text.count("\n", 0, match.start()) + 1
            matches.append(f"{path}:{line_number}: {match.group(0)!r}")

    assert matches == []
