"""
Phase B verification: ensure all 146 routes survived the monolith split.

Run inside Docker:
    docker run --rm -v ./bot/memory:/app/memory miku-discord-miku-bot \
        python -m pytest tests/test_route_split.py -v
"""

import pytest
import sys, os

# ── make /app importable ──
sys.path.insert(0, "/app")
os.chdir("/app")
os.environ.setdefault("DISCORD_BOT_TOKEN", "test_token")

# ── now import the FastAPI app ──
from api import app  # noqa: E402

# Collect all routes from the app
def _collect_routes():
    """Return set of (method, path) tuples registered on the FastAPI app."""
    routes = set()
    for route in app.routes:
        # Skip Mount routes (static files) and other non-API routes
        if not hasattr(route, "methods"):
            continue
        for method in route.methods:
            # Normalize: uppercase method, path as-is
            routes.add((method.upper(), route.path))
    return routes

REGISTERED = _collect_routes()


# ── Expected routes: every route from the original monolith ──
EXPECTED_ROUTES = [
    # core.py (7)
    ("GET", "/"),
    ("GET", "/logs"),
    ("GET", "/prompt"),
    ("GET", "/prompt/cat"),
    ("GET", "/status"),
    ("GET", "/autonomous/stats"),
    ("GET", "/conversation/{user_id}"),
    # mood.py (10)
    ("GET", "/mood"),
    ("POST", "/mood"),
    ("POST", "/mood/reset"),
    ("POST", "/mood/calm"),
    ("GET", "/servers/{guild_id}/mood"),
    ("POST", "/servers/{guild_id}/mood"),
    ("POST", "/servers/{guild_id}/mood/reset"),
    ("GET", "/servers/{guild_id}/mood/state"),
    ("GET", "/moods/available"),
    ("POST", "/test/mood/{guild_id}"),
    # language.py (3)
    ("GET", "/language"),
    ("POST", "/language/toggle"),
    ("POST", "/language/set"),
    # evil_mode.py (6)
    ("GET", "/evil-mode"),
    ("POST", "/evil-mode/enable"),
    ("POST", "/evil-mode/disable"),
    ("POST", "/evil-mode/toggle"),
    ("GET", "/evil-mode/mood"),
    ("POST", "/evil-mode/mood"),
    # bipolar_mode.py (9)
    ("GET", "/bipolar-mode"),
    ("POST", "/bipolar-mode/enable"),
    ("POST", "/bipolar-mode/disable"),
    ("POST", "/bipolar-mode/toggle"),
    ("POST", "/bipolar-mode/trigger-argument"),
    ("POST", "/bipolar-mode/trigger-dialogue"),
    ("GET", "/bipolar-mode/scoreboard"),
    ("POST", "/bipolar-mode/cleanup-webhooks"),
    ("GET", "/bipolar-mode/arguments"),
    # gpu.py (2)
    ("GET", "/gpu-status"),
    ("POST", "/gpu-select"),
    # bot_actions.py (4)
    ("POST", "/conversation/reset"),
    ("POST", "/sleep"),
    ("POST", "/wake"),
    ("POST", "/bedtime"),
    # autonomous.py (13)
    ("POST", "/autonomous/general"),
    ("POST", "/autonomous/engage"),
    ("POST", "/autonomous/tweet"),
    ("POST", "/autonomous/custom"),
    ("POST", "/autonomous/reaction"),
    ("POST", "/autonomous/join-conversation"),
    ("POST", "/servers/{guild_id}/autonomous/general"),
    ("POST", "/servers/{guild_id}/autonomous/engage"),
    ("POST", "/servers/{guild_id}/autonomous/custom"),
    ("POST", "/servers/{guild_id}/autonomous/tweet"),
    ("GET", "/autonomous/v2/stats/{guild_id}"),
    ("GET", "/autonomous/v2/check/{guild_id}"),
    ("GET", "/autonomous/v2/status"),
    # profile_picture.py (26)
    ("POST", "/profile-picture/change"),
    ("GET", "/profile-picture/metadata"),
    ("POST", "/profile-picture/restore-fallback"),
    ("POST", "/role-color/custom"),
    ("POST", "/role-color/reset-fallback"),
    ("GET", "/profile-picture/image/original"),
    ("GET", "/profile-picture/image/current"),
    ("POST", "/profile-picture/change-no-crop"),
    ("POST", "/profile-picture/manual-crop"),
    ("POST", "/profile-picture/auto-crop"),
    ("POST", "/profile-picture/description"),
    ("POST", "/profile-picture/regenerate-description"),
    ("GET", "/profile-picture/description"),
    ("GET", "/profile-picture/album"),
    ("GET", "/profile-picture/album/disk-usage"),
    ("GET", "/profile-picture/album/{entry_id}"),
    ("GET", "/profile-picture/album/{entry_id}/image/{image_type}"),
    ("POST", "/profile-picture/album/add"),
    ("POST", "/profile-picture/album/add-batch"),
    ("POST", "/profile-picture/album/{entry_id}/set-current"),
    ("POST", "/profile-picture/album/{entry_id}/manual-crop"),
    ("POST", "/profile-picture/album/{entry_id}/auto-crop"),
    ("POST", "/profile-picture/album/{entry_id}/description"),
    ("DELETE", "/profile-picture/album/{entry_id}"),
    ("POST", "/profile-picture/album/delete-bulk"),
    ("POST", "/profile-picture/album/add-current"),
    # manual_send.py (3)
    ("POST", "/manual/send"),
    ("POST", "/manual/send-webhook"),
    ("POST", "/messages/react"),
    # servers.py (6)
    ("GET", "/servers"),
    ("POST", "/servers"),
    ("DELETE", "/servers/{guild_id}"),
    ("PUT", "/servers/{guild_id}"),
    ("POST", "/servers/{guild_id}/bedtime-range"),
    ("POST", "/servers/repair"),
    # figurines.py (5)
    ("GET", "/figurines/subscribers"),
    ("POST", "/figurines/subscribers"),
    ("DELETE", "/figurines/subscribers/{user_id}"),
    ("POST", "/figurines/send_now"),
    ("POST", "/figurines/send_to_user"),
    # dms.py (18)
    ("POST", "/dm/{user_id}/custom"),
    ("POST", "/dm/{user_id}/manual"),
    ("GET", "/dms/users"),
    ("GET", "/dms/users/{user_id}"),
    ("GET", "/dms/users/{user_id}/conversations"),
    ("GET", "/dms/users/{user_id}/search"),
    ("GET", "/dms/users/{user_id}/export"),
    ("DELETE", "/dms/users/{user_id}"),
    ("GET", "/dms/blocked-users"),
    ("POST", "/dms/users/{user_id}/block"),
    ("POST", "/dms/users/{user_id}/unblock"),
    ("POST", "/dms/users/{user_id}/conversations/{conversation_id}/delete"),
    ("POST", "/dms/users/{user_id}/conversations/delete-all"),
    ("POST", "/dms/users/{user_id}/delete-completely"),
    ("POST", "/dms/analysis/run"),
    ("POST", "/dms/users/{user_id}/analyze"),
    ("GET", "/dms/analysis/reports"),
    ("GET", "/dms/analysis/reports/{user_id}"),
    # image_generation.py (4)
    ("POST", "/image/generate"),
    ("GET", "/image/status"),
    ("POST", "/image/test-detection"),
    ("GET", "/image/view/{filename}"),
    # chat.py (1)
    ("POST", "/chat/stream"),
    # config.py (7)
    ("GET", "/config"),
    ("GET", "/config/static"),
    ("GET", "/config/runtime"),
    ("POST", "/config/set"),
    ("POST", "/config/reset"),
    ("POST", "/config/validate"),
    ("GET", "/config/state"),
    # logging_config.py (9)
    ("GET", "/api/log/config"),
    ("POST", "/api/log/config"),
    ("GET", "/api/log/components"),
    ("POST", "/api/log/reload"),
    ("POST", "/api/log/filters"),
    ("POST", "/api/log/reset"),
    ("POST", "/api/log/global-level"),
    ("POST", "/api/log/timestamp-format"),
    ("GET", "/api/log/files/{component}"),
    # voice.py (3)
    ("POST", "/voice/call"),
    ("GET", "/voice/debug-mode"),
    ("POST", "/voice/debug-mode"),
    # memory.py (10)
    ("GET", "/memory/status"),
    ("POST", "/memory/toggle"),
    ("GET", "/memory/stats"),
    ("GET", "/memory/facts"),
    ("GET", "/memory/episodic"),
    ("POST", "/memory/consolidate"),
    ("POST", "/memory/delete"),
    ("DELETE", "/memory/point/{collection}/{point_id}"),
    ("PUT", "/memory/point/{collection}/{point_id}"),
    ("POST", "/memory/create"),
]


class TestRoutePresence:
    """Verify each expected route is registered on the FastAPI app."""

    @pytest.mark.parametrize("method,path", EXPECTED_ROUTES,
                             ids=[f"{m} {p}" for m, p in EXPECTED_ROUTES])
    def test_route_exists(self, method, path):
        assert (method, path) in REGISTERED, (
            f"Route {method} {path} missing from app.routes! "
            f"Registered routes with similar path: "
            f"{[r for r in REGISTERED if path.split('/')[1] in r[1]]}"
        )

    def test_total_route_count(self):
        """Sanity check: we expect exactly 146 API routes."""
        assert len(EXPECTED_ROUTES) == 146, f"Expected list has {len(EXPECTED_ROUTES)} routes, want 146"

    def test_no_unexpected_route_loss(self):
        """Every expected route must be registered."""
        missing = [(m, p) for m, p in EXPECTED_ROUTES if (m, p) not in REGISTERED]
        assert not missing, f"Missing {len(missing)} routes:\n" + "\n".join(f"  {m} {p}" for m, p in missing)

    def test_registered_count_at_least_expected(self):
        """Registered API routes should be >= expected (HEAD routes are auto-added)."""
        # Filter out HEAD duplicates that FastAPI adds automatically for GET routes
        non_head = {r for r in REGISTERED if r[0] != "HEAD"}
        assert len(non_head) >= 146, f"Only {len(non_head)} non-HEAD routes registered, expected >= 146"
