"""Smoke tests for critical startup and import paths."""

from __future__ import annotations

import subprocess
import sys
from pathlib import Path

from astrbot.core.pipeline.bootstrap import ensure_builtin_stages_registered
from astrbot.core.pipeline.process_stage.method.agent_sub_stages.internal import (
    InternalAgentSubStage,
)
from astrbot.core.pipeline.process_stage.method.agent_sub_stages.third_party import (
    ThirdPartyAgentSubStage,
)
from astrbot.core.pipeline.stage import Stage, registered_stages
from astrbot.core.pipeline.stage_order import STAGES_ORDER

REPO_ROOT = Path(__file__).resolve().parents[1]


def _run_code_in_fresh_interpreter(code: str, failure_message: str) -> None:
    proc = subprocess.run(
        [sys.executable, "-c", code],
        cwd=REPO_ROOT,
        capture_output=True,
        text=True,
        check=False,
    )
    assert proc.returncode == 0, (
        f"{failure_message}\nstdout:\n{proc.stdout}\nstderr:\n{proc.stderr}\n"
    )


def test_smoke_critical_imports_in_fresh_interpreter() -> None:
    code = (
        "import importlib;"
        "mods=["
        "'astrbot.core.core_lifecycle',"
        "'astrbot.core.astr_main_agent',"
        "'astrbot.core.pipeline.scheduler',"
        "'astrbot.core.pipeline.process_stage.method.agent_sub_stages.internal',"
        "'astrbot.core.pipeline.process_stage.method.agent_sub_stages.third_party'"
        "];"
        "[importlib.import_module(m) for m in mods]"
    )
    _run_code_in_fresh_interpreter(code, "Smoke import check failed.")


def test_smoke_pipeline_stage_registration_matches_order() -> None:
    ensure_builtin_stages_registered()
    stage_names = {cls.__name__ for cls in registered_stages}

    assert set(STAGES_ORDER).issubset(stage_names)
    assert len(stage_names) == len(registered_stages)


def test_smoke_agent_sub_stages_are_stage_subclasses() -> None:
    assert issubclass(InternalAgentSubStage, Stage)
    assert issubclass(ThirdPartyAgentSubStage, Stage)


def test_pipeline_package_exports_remain_compatible() -> None:
    import astrbot.core.pipeline as pipeline

    assert pipeline.ProcessStage is not None
    assert pipeline.RespondStage is not None
    assert isinstance(pipeline.STAGES_ORDER, list)
    assert "ProcessStage" in pipeline.STAGES_ORDER


def test_builtin_stage_bootstrap_is_idempotent() -> None:
    ensure_builtin_stages_registered()
    before_count = len(registered_stages)
    stage_names = {cls.__name__ for cls in registered_stages}

    expected_stage_names = {
        "WakingCheckStage",
        "WhitelistCheckStage",
        "SessionStatusCheckStage",
        "RateLimitStage",
        "ContentSafetyCheckStage",
        "PreProcessStage",
        "ProcessStage",
        "ResultDecorateStage",
        "RespondStage",
    }

    assert expected_stage_names.issubset(stage_names)

    ensure_builtin_stages_registered()
    assert len(registered_stages) == before_count


def test_pipeline_import_is_stable_with_mocked_apscheduler() -> None:
    """Regression: importing pipeline should not require cron/apscheduler modules."""
    code = (
        "import sys;"
        "from unittest.mock import MagicMock;"
        "mock_apscheduler = MagicMock();"
        "mock_apscheduler.schedulers = MagicMock();"
        "mock_apscheduler.schedulers.asyncio = MagicMock();"
        "mock_apscheduler.schedulers.background = MagicMock();"
        "mock_apscheduler.triggers = MagicMock();"
        "mock_apscheduler.triggers.cron = MagicMock();"
        "mock_apscheduler.triggers.date = MagicMock();"
        "sys.modules['apscheduler'] = mock_apscheduler;"
        "sys.modules['apscheduler.schedulers'] = mock_apscheduler.schedulers;"
        "sys.modules['apscheduler.schedulers.asyncio'] = mock_apscheduler.schedulers.asyncio;"
        "sys.modules['apscheduler.schedulers.background'] = mock_apscheduler.schedulers.background;"
        "sys.modules['apscheduler.triggers'] = mock_apscheduler.triggers;"
        "sys.modules['apscheduler.triggers.cron'] = mock_apscheduler.triggers.cron;"
        "sys.modules['apscheduler.triggers.date'] = mock_apscheduler.triggers.date;"
        "import astrbot.core.pipeline as pipeline;"
        "assert pipeline.ProcessStage is not None;"
        "assert pipeline.RespondStage is not None"
    )
    _run_code_in_fresh_interpreter(
        code,
        "Pipeline import should not depend on real apscheduler package.",
    )
