#!/usr/bin/env python3
"""Robot Framework helper for context strategy registry smoke tests."""

from __future__ import annotations

import sys
from collections.abc import Callable
from pathlib import Path

# Ensure src is on sys.path
_SRC = Path(__file__).resolve().parents[1] / "src"
if str(_SRC) not in sys.path:
    sys.path.insert(0, str(_SRC))

from cleveragents.application.services.strategy_registry import (  # noqa: E402
    StrategyRegistry,
)
from cleveragents.domain.models.acms.crp import ContextRequest  # noqa: E402
from cleveragents.domain.models.acms.strategy import (  # noqa: E402
    BackendSet,
    ContextStrategy,
    StrategyConfig,
)
from cleveragents.domain.models.acms.strategy_stubs import (  # noqa: E402
    BUILTIN_STRATEGY_CLASSES,
    DEFAULT_ENABLED_STRATEGIES,
)
from cleveragents.domain.models.acms.stubs import (  # noqa: E402
    InMemoryGraphBackend,
    InMemoryTextBackend,
    InMemoryVectorBackend,
)

# ---------------------------------------------------------------------------
# Commands
# ---------------------------------------------------------------------------


def _cmd_register_builtins() -> int:
    """Register all built-in strategies and verify count."""
    registry = StrategyRegistry()
    for cls in BUILTIN_STRATEGY_CLASSES:
        inst = cls()
        enabled = inst.name in DEFAULT_ENABLED_STRATEGIES
        registry.register(inst, config=StrategyConfig(enabled=enabled), is_builtin=True)
    registry.set_enabled(list(DEFAULT_ENABLED_STRATEGIES))

    if len(registry) != 6:
        print(f"strategy-fail: expected 6 strategies, got {len(registry)}")
        return 1
    print(f"strategy-ok: registered {len(registry)} strategies")

    enabled = registry.list_enabled()
    if len(enabled) != 3:
        print(f"strategy-fail: expected 3 enabled, got {len(enabled)}")
        return 1
    print(f"strategy-enabled: {enabled}")
    return 0


def _cmd_protocol_check() -> int:
    """Verify all built-in strategies satisfy ContextStrategy protocol."""
    for cls in BUILTIN_STRATEGY_CLASSES:
        inst = cls()
        if not isinstance(inst, ContextStrategy):
            print(f"strategy-fail: {cls.__name__} doesn't satisfy protocol")
            return 1
        if not inst.name:
            print(f"strategy-fail: {cls.__name__} has empty name")
            return 1
        if inst.capabilities.quality_score <= 0:
            print(f"strategy-fail: {cls.__name__} has invalid quality score")
            return 1
        print(f"strategy-ok: {inst.name} (quality={inst.capabilities.quality_score})")
    return 0


def _cmd_can_handle() -> int:
    """Test can_handle with various backend configurations."""
    from cleveragents.domain.models.acms.strategy_stubs import (
        ARCEStrategy,
        SimpleKeywordStrategy,
    )

    request = ContextRequest(query="test")

    # simple-keyword with text backend
    sk = SimpleKeywordStrategy()
    bs_text = BackendSet(text=InMemoryTextBackend())
    score = sk.can_handle(request, bs_text)
    if abs(score - 0.3) > 1e-6:
        print(f"strategy-fail: simple-keyword expected 0.3, got {score}")
        return 1
    print(f"strategy-ok: simple-keyword can_handle with text = {score}")

    # simple-keyword without backend
    bs_none = BackendSet()
    score = sk.can_handle(request, bs_none)
    if score != 0.0:
        print(
            f"strategy-fail: simple-keyword expected 0.0 without backend, got {score}"
        )
        return 1
    print(f"strategy-ok: simple-keyword can_handle without backend = {score}")

    # arce with all backends
    arce = ARCEStrategy()
    bs_all = BackendSet(
        text=InMemoryTextBackend(),
        vector=InMemoryVectorBackend(),
        graph=InMemoryGraphBackend(),
    )
    score = arce.can_handle(request, bs_all)
    if abs(score - 0.95) > 1e-6:
        print(f"strategy-fail: arce expected 0.95, got {score}")
        return 1
    print(f"strategy-ok: arce can_handle with all = {score}")

    return 0


def _cmd_validate() -> int:
    """Validate registry with all built-in strategies."""
    registry = StrategyRegistry()
    for cls in BUILTIN_STRATEGY_CLASSES:
        registry.register(cls(), is_builtin=True)

    warnings = registry.validate_registry()
    if warnings:
        print(f"strategy-fail: unexpected warnings: {warnings}")
        return 1
    print("strategy-ok: registry validation passed with no warnings")
    return 0


def _cmd_pipeline_integration() -> int:
    """Integration test: verify all 6 spec strategies are registered in ACMSPipeline.

    Verifies that the 6 spec-required strategies are registered with ACMSPipeline
    and that the pipeline can be used with the spec strategy names.
    """
    from cleveragents.application.services.acms_service import ACMSPipeline

    pipeline = ACMSPipeline()

    # Verify all 6 spec-required strategies are registered
    expected_strategies = {
        "simple-keyword",
        "semantic-embedding",
        "breadth-depth-navigator",
        "arce",
        "temporal-archaeology",
        "plan-decision-context",
    }
    registered = set(pipeline._strategies.keys())
    missing = expected_strategies - registered
    if missing:
        print(f"strategy-fail: missing strategies: {missing}")
        return 1

    print("strategy-ok: all 6 spec strategies registered in ACMSPipeline")
    print(f"strategy-ok: total strategies: {len(registered)}")
    return 0


def _cmd_real_retrieval() -> int:
    """Test that each strategy returns non-empty fragments with populated backends."""
    from datetime import UTC, datetime, timedelta

    from cleveragents.domain.models.acms.backends import (
        GraphResult,
        TextResult,
        VectorResult,
    )
    from cleveragents.domain.models.acms.crp import ContextRequest
    from cleveragents.domain.models.acms.strategy import BackendSet, PlanContext
    from cleveragents.domain.models.acms.strategy_stubs import BUILTIN_STRATEGY_CLASSES
    from cleveragents.domain.models.acms.temporal import TemporalMetadata, TemporalNode
    from cleveragents.domain.models.acms.temporal_stubs import InMemoryTemporalBackend

    class _TextBackend:
        def search(self, query, *, scope, max_results=20):
            return [
                TextResult(
                    uko_uri="uko:class/Auth",
                    content=f"Auth for {query}",
                    score=0.9,
                )
            ]

    class _VectorBackend:
        def similarity_search(self, embedding, *, scope, top_k=20):
            return [
                VectorResult(
                    uko_uri="uko:class/Auth",
                    content="Auth semantic",
                    score=0.85,
                )
            ]

    class _GraphBackend:
        def sparql_query(self, query, *, scope):
            return GraphResult(triples=[("uko:class/Auth", "rdf:type", "uko:Class")])

        def get_triples(self, subject):
            return GraphResult(triples=[(subject, "rdf:type", "uko:Class")])

        def traverse(self, start, *, depth=2):
            return GraphResult(
                triples=[
                    (start, "rdf:type", "uko:Class"),
                    (start, "uko:hasMethod", "uko:method/auth"),
                ]
            )

    # Create temporal backend with data
    temporal_backend = InMemoryTemporalBackend()
    now = datetime.now(tz=UTC)
    node = TemporalNode(
        node_uri="uko:plan/test_v1",
        source_resource="res://test",
        source_path="src/test.py",
        temporal=TemporalMetadata(
            valid_from=now - timedelta(hours=1),
            is_current=True,
        ),
    )
    temporal_backend.store_node(node)

    request = ContextRequest(query="authentication", focus=["uko:class/Auth"])
    plan_context = PlanContext()

    backend_map = {
        "simple-keyword": BackendSet(text=_TextBackend()),
        "semantic-embedding": BackendSet(vector=_VectorBackend()),
        "breadth-depth-navigator": BackendSet(graph=_GraphBackend()),
        "arce": BackendSet(
            text=_TextBackend(),
            vector=_VectorBackend(),
            graph=_GraphBackend(),
        ),
        "temporal-archaeology": BackendSet(
            graph=_GraphBackend(),
            temporal=temporal_backend,
        ),
        "plan-decision-context": BackendSet(temporal=temporal_backend),
    }

    all_ok = True
    for cls in BUILTIN_STRATEGY_CLASSES:
        strategy = cls()
        backends = backend_map[strategy.name]
        result = strategy.assemble(request, backends, 10000, plan_context)
        if len(result) < 1:
            print(
                f"strategy-fail: {strategy.name} returned 0 fragments "
                "with populated backends"
            )
            all_ok = False
        else:
            print(f"strategy-ok: {strategy.name} returned {len(result)} fragment(s)")

    return 0 if all_ok else 1


_COMMANDS: dict[str, Callable[[], int]] = {
    "register-builtins": _cmd_register_builtins,
    "protocol-check": _cmd_protocol_check,
    "can-handle": _cmd_can_handle,
    "validate": _cmd_validate,
    "pipeline-integration": _cmd_pipeline_integration,
    "real-retrieval": _cmd_real_retrieval,
}


def main() -> int:
    """Run the specified command."""
    if len(sys.argv) < 2 or sys.argv[1] not in _COMMANDS:
        print(f"Usage: {sys.argv[0]} <{'|'.join(_COMMANDS)}>")
        return 2
    return _COMMANDS[sys.argv[1]]()


if __name__ == "__main__":
    sys.exit(main())
