"""Tests for lambdagent.fromconfig compiler."""
import os
import json
import tempfile
import pytest
import yaml
from lambdagent.fromconfig import from_config, lint_config, to_lambda_expr, describe_config
from lambdagent.fromconfig.errors import SchemaError
from lambdagent.fromconfig.schema import validate_schema
from lambdagent.primitives import Lam, Loop, Compose
from lambdagent.extensions import Memory, Guard, Route
from lambdagent.conversation import ConversationLam


def _write_config(config):
    f = tempfile.NamedTemporaryFile(mode='w', suffix='.yml', delete=False, encoding='utf-8')
    yaml.dump(config, f, allow_unicode=True)
    f.close()
    return f.name


def test_compile_simple():
    path = _write_config({"type": "simple", "systemPrompt": "Hello", "model": {"name": "test"}})
    try:
        term = from_config(path)
        # The compiler may emit either Lam (stateless) or ConversationLam
        # (history-aware) for a "simple" agent depending on provider config.
        # Both are valid Lambda abstractions — accept either.
        assert isinstance(term, (Lam, ConversationLam)), \
            f"expected Lam or ConversationLam, got {type(term).__name__}"
    finally:
        os.unlink(path)


def test_compile_chain():
    path = _write_config({
        "type": "chain", "model": {"name": "test"},
        "chain": {"steps": [
            {"name": "s1", "prompt": "Step 1"},
            {"name": "s2", "prompt": "Step 2"},
        ]}
    })
    try:
        term = from_config(path)
        assert isinstance(term, Compose)
        assert len(term.stages) == 2
    finally:
        os.unlink(path)


def test_compile_react():
    path = _write_config({
        "type": "react", "systemPrompt": "Think",
        "model": {"name": "test"},
        "react": {"maxSteps": 5},
        "mcp": {"localTools": ["terminate"]},
    })
    try:
        term = from_config(path)
        assert isinstance(term, Loop)
        assert term.max_steps == 5
    finally:
        os.unlink(path)


def test_compile_with_memory():
    path = _write_config({
        "type": "simple", "systemPrompt": "Hello",
        "model": {"name": "test"},
        "memory": {"enabled": True, "strategy": "local", "size": 10, "ttl": 3600},
    })
    try:
        term = from_config(path)
        assert isinstance(term, Memory)
    finally:
        os.unlink(path)


def test_compile_with_guard():
    path = _write_config({
        "type": "simple", "systemPrompt": "Hello",
        "model": {"name": "test"},
        "guard": {"validator": "len(x) > 5", "retry": 1},
    })
    try:
        term = from_config(path)
        assert isinstance(term, Guard)
    finally:
        os.unlink(path)


def test_schema_validation_missing_type():
    errors = validate_schema({})
    assert any(e[0] == "ERROR" for e in errors)


def test_lint_no_terminate():
    results = lint_config({
        "type": "react", "systemPrompt": "x",
        "react": {"maxSteps": 10},
        "mcp": {"localTools": []},
    })
    rules = [r.rule for r in results]
    assert any("L004" in r for r in rules)


def test_lint_clean():
    results = lint_config({
        "type": "react", "systemPrompt": "x",
        "model": {"name": "test"},
        "react": {"maxSteps": 10},
        "mcp": {"localTools": ["terminate"]},
    })
    errors = [r for r in results if r.level == "ERROR"]
    assert len(errors) == 0


def test_to_lambda_expr():
    expr = to_lambda_expr({"type": "simple", "name": "test", "systemPrompt": "hi"})
    assert "test" in expr
    assert "lambda" in expr


def test_describe_config():
    desc = describe_config({"type": "react", "name": "agent", "react": {"maxSteps": 10},
                            "mcp": {"onlineTool": {"s": ["search"]}, "localTools": ["terminate"]}})
    assert "Y_10" in desc or "Loop" in desc
